public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [AppArmor 00/47] AppArmor security module overview
@ 2007-12-20 14:09 John, Johansen
  2007-12-20 14:09 ` [AppArmor 01/47] Pass struct vfsmount to the inode_create LSM hook John, Johansen
                   ` (46 more replies)
  0 siblings, 47 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm; +Cc: linux-kernel, linux-security-module

-- 

This submission of the AppArmor security module is based against 2.6.24-rc4-mm.

Any comments and feedback to improve implementation are appreciated.

Changes since previous submission
- added apparmor security goal document.
  Documentation/lsm/AppArmor-Security-Goal.txt
- removed DAC style permissions in favor of a simpler file owner
  permissions specification
- include the fgetattr and fsetattr patches by Miklos Szeredi
  <mszeredi@suse.cz>, and update them to use ATTR_FILE to enable LSMs to
  distinguish file descriptor operations
- fix error where a NULL sock passed to socket_getsocket_getpeersec_dgram()
  was not correctly handled.
- fix error in link permission subset test

Outstanding Issues
- use of d_namespace_path and buffer allocation to obtain a pathname for
  mediation.
- conditional passing of the vfsmnt.  This can be addressed by rebasing
  on the lookup intent patches but that has not been done for this
  submission.
- ipc and signal mediation are a wip and not included.
- fine grained network mediation
- system confinement from boot is a wip and not included.
- documentation needs to be updated to include newest features


The patch series consists of five areas:

 (1) Pass struct vfsmount through to LSM hooks.

 (2) Fixes and improvements to __d_path():

     (a) make it unambiguous and exclude unreachable paths from
         /proc/mounts,

     (b) make its result consistent in the face of remounts,

     (c) introduce d_namespace_path(), a variant of d_path that goes up
         to the namespace root instead of the chroot.

     (d) the behavior of d_path() and getcwd() remain unchanged, and
     there is no hidding of unreachable paths in /proc/mounts.  The
     patches addressing these have been seperated from the AppArmor
     submission and will be introduced at a later date.
 
     Part (a) has been in the -mm tree for a while; this series includes
     an updated copy of the -mm patch. Parts (b) and (c) shouldn't be too
     controversial.

 (3) Be able to distinguish file descriptor access from access by name
     in LSM hooks.

     Applications expect different behavior from file descriptor
     accesses and accesses by name in some cases. We need to pass this
     information down the LSM hooks to allow AppArmor to tell which is
     which.

 (4) Convert the selinux sysctl pathname computation code into a standalone
     function.

 (5) The AppArmor LSM itself.

     (See below.)

A tarball of the kernel patches, base user-space utilities, example
profiles, and technical documentation (including a walk-through) are
available at:

  http://forgeftp.novell.com/apparmor/LKML_Submission-Dec-07/

Only the most recent features are covered in brief here for a more
complete explaination please refere to the technical documentation.


File ownership permissions
  The DAC style permissions mask allowing the specification of permission
  for each of user, group, and other have been removed, after further feed
  back and discussion, in favor of a simpler permission set that allows
  specifying permissions for file ownership as determined by fsuid.

  Traditional AppArmor rules map to specifying permissions for files
  all files, to reduce the permissions grant the owner keyword can
  be added to a rule.

  /foo rw,		# allow access to file /foo
  owner /foo rw,	# allow access to file /foo only if its uid == fsuid



^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 01/47] Pass struct vfsmount to the inode_create LSM hook
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 02/47] Pass struct path down to remove_suid and children John, Johansen
                   ` (45 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Tony Jones,
	Andreas Gruenbacher, John Johansen

[-- Attachment #1: security-create.diff --]
[-- Type: text/plain, Size: 3973 bytes --]

This is needed for computing pathnames in the AppArmor LSM.

Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/namei.c               |    2 +-
 include/linux/security.h |    9 ++++++---
 security/dummy.c         |    2 +-
 security/security.c      |    5 +++--
 security/selinux/hooks.c |    3 ++-
 5 files changed, 13 insertions(+), 8 deletions(-)

--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1586,7 +1586,7 @@ int vfs_create(struct inode *dir, struct
 		return -EACCES;	/* shouldn't it be ENOSYS? */
 	mode &= S_IALLUGO;
 	mode |= S_IFREG;
-	error = security_inode_create(dir, dentry, mode);
+	error = security_inode_create(dir, dentry, nd ? nd->path.mnt : NULL, mode);
 	if (error)
 		return error;
 	DQUOT_INIT(dir);
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -314,6 +314,7 @@ struct request_sock;
  *	Check permission to create a regular file.
  *	@dir contains inode structure of the parent of the new file.
  *	@dentry contains the dentry structure for the file to be created.
+ *	@mnt is the vfsmount corresponding to @dentry (may be NULL).
  *	@mode contains the file mode of the file to be created.
  *	Return 0 if permission is granted.
  * @inode_link:
@@ -1272,8 +1273,8 @@ struct security_operations {
 	void (*inode_free_security) (struct inode *inode);
 	int (*inode_init_security) (struct inode *inode, struct inode *dir,
 				    char **name, void **value, size_t *len);
-	int (*inode_create) (struct inode *dir,
-	                     struct dentry *dentry, int mode);
+	int (*inode_create) (struct inode *dir, struct dentry *dentry,
+			     struct vfsmount *mnt, int mode);
 	int (*inode_link) (struct dentry *old_dentry,
 	                   struct inode *dir, struct dentry *new_dentry);
 	int (*inode_unlink) (struct inode *dir, struct dentry *dentry);
@@ -1536,7 +1537,8 @@ int security_inode_alloc(struct inode *i
 void security_inode_free(struct inode *inode);
 int security_inode_init_security(struct inode *inode, struct inode *dir,
 				  char **name, void **value, size_t *len);
-int security_inode_create(struct inode *dir, struct dentry *dentry, int mode);
+int security_inode_create(struct inode *dir, struct dentry *dentry,
+			  struct vfsmount *mnt, int mode);
 int security_inode_link(struct dentry *old_dentry, struct inode *dir,
 			 struct dentry *new_dentry);
 int security_inode_unlink(struct inode *dir, struct dentry *dentry);
@@ -1847,6 +1849,7 @@ static inline int security_inode_init_se
 	
 static inline int security_inode_create (struct inode *dir,
 					 struct dentry *dentry,
+					 struct vfsmount *mnt,
 					 int mode)
 {
 	return 0;
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -290,7 +290,7 @@ static int dummy_inode_init_security (st
 }
 
 static int dummy_inode_create (struct inode *inode, struct dentry *dentry,
-			       int mask)
+			       struct vfsmount *mnt, int mask)
 {
 	return 0;
 }
--- a/security/security.c
+++ b/security/security.c
@@ -348,11 +348,12 @@ int security_inode_init_security(struct 
 }
 EXPORT_SYMBOL(security_inode_init_security);
 
-int security_inode_create(struct inode *dir, struct dentry *dentry, int mode)
+int security_inode_create(struct inode *dir, struct dentry *dentry,
+			  struct vfsmount *mnt, int mode)
 {
 	if (unlikely(IS_PRIVATE(dir)))
 		return 0;
-	return security_ops->inode_create(dir, dentry, mode);
+	return security_ops->inode_create(dir, dentry, mnt, mode);
 }
 
 int security_inode_link(struct dentry *old_dentry, struct inode *dir,
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2411,7 +2411,8 @@ static int selinux_inode_init_security(s
 	return 0;
 }
 
-static int selinux_inode_create(struct inode *dir, struct dentry *dentry, int mask)
+static int selinux_inode_create(struct inode *dir, struct dentry *dentry,
+				 struct vfsmount *mnt, int mask)
 {
 	return may_create(dir, dentry, SECCLASS_FILE);
 }

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 02/47] Pass struct path down to remove_suid and children
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
  2007-12-20 14:09 ` [AppArmor 01/47] Pass struct vfsmount to the inode_create LSM hook John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 03/47] Add a vfsmount parameter to notify_change() John, Johansen
                   ` (44 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Tony Jones,
	Andreas Gruenbacher, John Johansen

[-- Attachment #1: remove_suid.diff --]
[-- Type: text/plain, Size: 4651 bytes --]

Required by a later patch that adds a struct vfsmount parameter to
notify_change().

Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---

 fs/ntfs/file.c                         |    2 +-
 fs/reiser4/plugin/file/cryptcompress.c |    2 +-
 fs/reiser4/plugin/file/file.c          |    2 +-
 fs/splice.c                            |    4 ++--
 fs/xfs/linux-2.6/xfs_lrw.c             |    2 +-
 include/linux/fs.h                     |    4 ++--
 mm/filemap.c                           |   16 ++++++++--------
 mm/filemap_xip.c                       |    2 +-
 8 files changed, 17 insertions(+), 17 deletions(-)

--- a/fs/ntfs/file.c
+++ b/fs/ntfs/file.c
@@ -2118,7 +2118,7 @@ static ssize_t ntfs_file_aio_write_noloc
 		goto out;
 	if (!count)
 		goto out;
-	err = remove_suid(file->f_path.dentry);
+	err = remove_suid(&file->f_path);
 	if (err)
 		goto out;
 	file_update_time(file);
--- a/fs/reiser4/plugin/file/cryptcompress.c
+++ b/fs/reiser4/plugin/file/cryptcompress.c
@@ -2828,7 +2828,7 @@ ssize_t write_cryptcompress(struct file 
 		goto out;
   	if (unlikely(count == 0))
 		goto out;
-	result = remove_suid(file->f_dentry);
+	result = remove_suid(&file->f_path);
 	if (unlikely(result != 0))
 		goto out;
 	/* remove_suid might create a transaction */
--- a/fs/reiser4/plugin/file/file.c
+++ b/fs/reiser4/plugin/file/file.c
@@ -2124,7 +2124,7 @@ ssize_t write_unix_file(struct file *fil
 		return result;
 	}
 
-	result = remove_suid(file->f_dentry);
+	result = remove_suid(&file->f_path);
 	if (result) {
 		mutex_unlock(&inode->i_mutex);
 		context_set_commit_async(ctx);
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -781,7 +781,7 @@ generic_file_splice_write_nolock(struct 
 	ssize_t ret;
 	int err;
 
-	err = remove_suid(out->f_path.dentry);
+	err = remove_suid(&out->f_path);
 	if (unlikely(err))
 		return err;
 
@@ -841,7 +841,7 @@ generic_file_splice_write(struct pipe_in
 		if (killpriv)
 			err = security_inode_killpriv(out->f_path.dentry);
 		if (!err && killsuid)
-			err = __remove_suid(out->f_path.dentry, killsuid);
+			err = __remove_suid(&out->f_path, killsuid);
 		mutex_unlock(&inode->i_mutex);
 		if (err)
 			return err;
--- a/fs/xfs/linux-2.6/xfs_lrw.c
+++ b/fs/xfs/linux-2.6/xfs_lrw.c
@@ -723,7 +723,7 @@ start:
 	     !capable(CAP_FSETID)) {
 		error = xfs_write_clear_setuid(xip);
 		if (likely(!error))
-			error = -remove_suid(file->f_path.dentry);
+			error = -remove_suid(&file->f_path);
 		if (unlikely(error)) {
 			goto out_unlock_internal;
 		}
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1757,9 +1757,9 @@ extern void iget_failed(struct inode *);
 extern void clear_inode(struct inode *);
 extern void destroy_inode(struct inode *);
 extern struct inode *new_inode(struct super_block *);
-extern int __remove_suid(struct dentry *, int);
+extern int __remove_suid(struct path *, int);
 extern int should_remove_suid(struct dentry *);
-extern int remove_suid(struct dentry *);
+extern int remove_suid(struct path *);
 
 extern void __insert_inode_hash(struct inode *, unsigned long hashval);
 extern void remove_inode_hash(struct inode *);
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -1638,26 +1638,26 @@ int should_remove_suid(struct dentry *de
 }
 EXPORT_SYMBOL(should_remove_suid);
 
-int __remove_suid(struct dentry *dentry, int kill)
+int __remove_suid(struct path *path, int kill)
 {
 	struct iattr newattrs;
 
 	newattrs.ia_valid = ATTR_FORCE | kill;
-	return notify_change(dentry, &newattrs);
+	return notify_change(path->dentry, &newattrs);
 }
 
-int remove_suid(struct dentry *dentry)
+int remove_suid(struct path *path)
 {
-	int killsuid = should_remove_suid(dentry);
-	int killpriv = security_inode_need_killpriv(dentry);
+	int killsuid = should_remove_suid(path->dentry);
+	int killpriv = security_inode_need_killpriv(path->dentry);
 	int error = 0;
 
 	if (killpriv < 0)
 		return killpriv;
 	if (killpriv)
-		error = security_inode_killpriv(dentry);
+		error = security_inode_killpriv(path->dentry);
 	if (!error && killsuid)
-		error = __remove_suid(dentry, killsuid);
+		error = __remove_suid(path, killsuid);
 
 	return error;
 }
@@ -2370,7 +2370,7 @@ __generic_file_aio_write_nolock(struct k
 	if (count == 0)
 		goto out;
 
-	err = remove_suid(file->f_path.dentry);
+	err = remove_suid(&file->f_path);
 	if (err)
 		goto out;
 
--- a/mm/filemap_xip.c
+++ b/mm/filemap_xip.c
@@ -378,7 +378,7 @@ xip_file_write(struct file *filp, const 
 	if (count == 0)
 		goto out_backing;
 
-	ret = remove_suid(filp->f_path.dentry);
+	ret = remove_suid(&filp->f_path);
 	if (ret)
 		goto out_backing;
 

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 03/47] Add a vfsmount parameter to notify_change()
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
  2007-12-20 14:09 ` [AppArmor 01/47] Pass struct vfsmount to the inode_create LSM hook John, Johansen
  2007-12-20 14:09 ` [AppArmor 02/47] Pass struct path down to remove_suid and children John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 04/47] Pass struct vfsmount to the inode_setattr LSM hook John, Johansen
                   ` (43 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Tony Jones,
	Andreas Gruenbacher, John Johansen

[-- Attachment #1: vfs-notify_change.diff --]
[-- Type: text/plain, Size: 22110 bytes --]

The vfsmount parameter must be set appropriately for files visibile
outside the kernel. Files that are only used in a filesystem (e.g.,
reiserfs xattr files) will have a NULL vfsmount.

Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/attr.c           |    3 ++-
 fs/ecryptfs/inode.c |    4 +++-
 fs/exec.c           |    3 ++-
 fs/fat/file.c       |    2 +-
 fs/hpfs/namei.c     |    2 +-
 fs/namei.c          |    2 +-
 fs/nfsd/vfs.c       |    8 ++++----
 fs/open.c           |   29 ++++++++++++++++-------------
 fs/reiserfs/xattr.c |    6 +++---
 fs/sysfs/file.c     |    2 +-
 fs/unionfs/copyup.c |   22 +++++++++++++++-------
 fs/unionfs/inode.c  |   23 ++++++++++++++++-------
 fs/unionfs/rename.c |    3 ++-
 fs/unionfs/subr.c   |    3 ++-
 fs/unionfs/union.h  |    3 ++-
 fs/utimes.c         |   19 +++++++++----------
 include/linux/fs.h  |    6 +++---
 mm/filemap.c        |    2 +-
 mm/tiny-shmem.c     |    2 +-
 19 files changed, 85 insertions(+), 59 deletions(-)

--- a/fs/attr.c
+++ b/fs/attr.c
@@ -100,7 +100,8 @@ int inode_setattr(struct inode * inode, 
 }
 EXPORT_SYMBOL(inode_setattr);
 
-int notify_change(struct dentry * dentry, struct iattr * attr)
+int notify_change(struct dentry *dentry, struct vfsmount *mnt,
+		  struct iattr *attr)
 {
 	struct inode *inode = dentry->d_inode;
 	mode_t mode = inode->i_mode;
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -860,6 +860,7 @@ static int ecryptfs_setattr(struct dentr
 {
 	int rc = 0;
 	struct dentry *lower_dentry;
+	struct vfsmount *lower_mnt;
 	struct inode *inode;
 	struct inode *lower_inode;
 	struct ecryptfs_crypt_stat *crypt_stat;
@@ -870,6 +871,7 @@ static int ecryptfs_setattr(struct dentr
 	inode = dentry->d_inode;
 	lower_inode = ecryptfs_inode_to_lower(inode);
 	lower_dentry = ecryptfs_dentry_to_lower(dentry);
+	lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry);
 	mutex_lock(&crypt_stat->cs_mutex);
 	if (S_ISDIR(dentry->d_inode->i_mode))
 		crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED);
@@ -920,7 +922,7 @@ static int ecryptfs_setattr(struct dentr
 	if (ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID))
 		ia->ia_valid &= ~ATTR_MODE;
 
-	rc = notify_change(lower_dentry, ia);
+	rc = notify_change(lower_dentry, lower_mnt, ia);
 out:
 	fsstack_copy_attr_all(inode, lower_inode);
 	return rc;
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1786,7 +1786,8 @@ int do_coredump(long signr, int exit_cod
 		goto close_fail;
 	if (!file->f_op->write)
 		goto close_fail;
-	if (!ispipe && do_truncate(file->f_path.dentry, 0, 0, file) != 0)
+	if (!ispipe &&
+	    do_truncate(file->f_path.dentry, file->f_path.mnt, 0, 0, file) != 0)
 		goto close_fail;
 
 	retval = binfmt->core_dump(signr, regs, file, core_limit);
--- a/fs/fat/file.c
+++ b/fs/fat/file.c
@@ -92,7 +92,7 @@ int fat_generic_ioctl(struct inode *inod
 		}
 
 		/* This MUST be done before doing anything irreversible... */
-		err = notify_change(filp->f_path.dentry, &ia);
+		err = notify_change(filp->f_path.dentry, filp->f_path.mnt, &ia);
 		if (err)
 			goto up;
 
--- a/fs/hpfs/namei.c
+++ b/fs/hpfs/namei.c
@@ -426,7 +426,7 @@ again:
 			/*printk("HPFS: truncating file before delete.\n");*/
 			newattrs.ia_size = 0;
 			newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
-			err = notify_change(dentry, &newattrs);
+			err = notify_change(dentry, NULL, &newattrs);
 			put_write_access(inode);
 			if (!err)
 				goto again;
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1663,7 +1663,7 @@ int may_open(struct nameidata *nd, int a
 		if (!error) {
 			DQUOT_INIT(inode);
 
-			error = do_truncate(dentry, 0,
+			error = do_truncate(dentry, nd->path.mnt, 0,
 					    ATTR_MTIME|ATTR_CTIME|ATTR_OPEN,
 					    NULL);
 		}
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -388,7 +388,7 @@ nfsd_setattr(struct svc_rqst *rqstp, str
 	err = nfserr_notsync;
 	if (!check_guard || guardtime == inode->i_ctime.tv_sec) {
 		fh_lock(fhp);
-		host_err = notify_change(dentry, iap);
+		host_err = notify_change(dentry, fhp->fh_export->ex_path.mnt, iap);
 		err = nfserrno(host_err);
 		fh_unlock(fhp);
 	}
@@ -944,13 +944,13 @@ out:
 	return err;
 }
 
-static void kill_suid(struct dentry *dentry)
+static void kill_suid(struct dentry *dentry, struct vfsmount *mnt)
 {
 	struct iattr	ia;
 	ia.ia_valid = ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
 
 	mutex_lock(&dentry->d_inode->i_mutex);
-	notify_change(dentry, &ia);
+	notify_change(dentry, mnt, &ia);
 	mutex_unlock(&dentry->d_inode->i_mutex);
 }
 
@@ -1009,7 +1009,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, s
 
 	/* clear setuid/setgid flag after write */
 	if (host_err >= 0 && (inode->i_mode & (S_ISUID | S_ISGID)))
-		kill_suid(dentry);
+		kill_suid(dentry, exp->ex_path.mnt);
 
 	if (host_err >= 0 && stable) {
 		static ino_t	last_ino;
--- a/fs/open.c
+++ b/fs/open.c
@@ -194,8 +194,8 @@ out:
 	return error;
 }
 
-int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
-	struct file *filp)
+int do_truncate(struct dentry *dentry, struct vfsmount *mnt, loff_t length,
+		unsigned int time_attrs, struct file *filp)
 {
 	int err;
 	struct iattr newattrs;
@@ -215,7 +215,7 @@ int do_truncate(struct dentry *dentry, l
 	newattrs.ia_valid |= should_remove_suid(dentry);
 
 	mutex_lock(&dentry->d_inode->i_mutex);
-	err = notify_change(dentry, &newattrs);
+	err = notify_change(dentry, mnt, &newattrs);
 	mutex_unlock(&dentry->d_inode->i_mutex);
 	return err;
 }
@@ -271,7 +271,8 @@ static long do_sys_truncate(const char _
 	error = locks_verify_truncate(inode, NULL, length);
 	if (!error) {
 		DQUOT_INIT(inode);
-		error = do_truncate(nd.path.dentry, length, 0, NULL);
+		error = do_truncate(nd.path.dentry, nd.path.mnt, length, 0,
+				    NULL);
 	}
 
 put_write_and_out:
@@ -326,7 +327,8 @@ static long do_sys_ftruncate(unsigned in
 
 	error = locks_verify_truncate(inode, file, length);
 	if (!error)
-		error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, file);
+		error = do_truncate(dentry, file->f_path.mnt, length,
+				    ATTR_MTIME|ATTR_CTIME, file);
 out_putf:
 	fput(file);
 out:
@@ -589,7 +591,7 @@ asmlinkage long sys_fchmod(unsigned int 
 		mode = inode->i_mode;
 	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
 	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
-	err = notify_change(dentry, &newattrs);
+	err = notify_change(dentry, file->f_path.mnt, &newattrs);
 	mutex_unlock(&inode->i_mutex);
 
 out_drop_write:
@@ -626,7 +628,7 @@ asmlinkage long sys_fchmodat(int dfd, co
 		mode = inode->i_mode;
 	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
 	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
-	error = notify_change(nd.path.dentry, &newattrs);
+	error = notify_change(nd.path.dentry, nd.path.mnt, &newattrs);
 	mutex_unlock(&inode->i_mutex);
 
 out_drop_write:
@@ -642,7 +644,8 @@ asmlinkage long sys_chmod(const char __u
 	return sys_fchmodat(AT_FDCWD, filename, mode);
 }
 
-static int chown_common(struct dentry * dentry, uid_t user, gid_t group)
+static int chown_common(struct dentry * dentry, struct vfsmount *mnt,
+			uid_t user, gid_t group)
 {
 	struct inode * inode;
 	int error;
@@ -669,7 +672,7 @@ static int chown_common(struct dentry * 
 		newattrs.ia_valid |=
 			ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
 	mutex_lock(&inode->i_mutex);
-	error = notify_change(dentry, &newattrs);
+	error = notify_change(dentry, mnt, &newattrs);
 	mutex_unlock(&inode->i_mutex);
 out:
 	return error;
@@ -686,7 +689,7 @@ asmlinkage long sys_chown(const char __u
 	error = mnt_want_write(nd.path.mnt);
 	if (error)
 		goto out_release;
-	error = chown_common(nd.path.dentry, user, group);
+	error = chown_common(nd.path.dentry, nd.path.mnt, user, group);
 	mnt_drop_write(nd.path.mnt);
 out_release:
 	path_put(&nd.path);
@@ -711,7 +714,7 @@ asmlinkage long sys_fchownat(int dfd, co
 	error = mnt_want_write(nd.path.mnt);
 	if (error)
 		goto out_release;
-	error = chown_common(nd.path.dentry, user, group);
+	error = chown_common(nd.path.dentry, nd.path.mnt, user, group);
 	mnt_drop_write(nd.path.mnt);
 out_release:
 	path_put(&nd.path);
@@ -730,7 +733,7 @@ asmlinkage long sys_lchown(const char __
 	error = mnt_want_write(nd.path.mnt);
 	if (error)
 		goto out_release;
-	error = chown_common(nd.path.dentry, user, group);
+	error = chown_common(nd.path.dentry, nd.path.mnt, user, group);
 	mnt_drop_write(nd.path.mnt);
 out_release:
 	path_put(&nd.path);
@@ -754,7 +757,7 @@ asmlinkage long sys_fchown(unsigned int 
 		goto out_fput;
 	dentry = file->f_path.dentry;
 	audit_inode(NULL, dentry);
-	error = chown_common(dentry, user, group);
+	error = chown_common(dentry, file->f_path.mnt, user, group);
 	mnt_drop_write(file->f_vfsmnt);
 out_fput:
 	fput(file);
--- a/fs/reiserfs/xattr.c
+++ b/fs/reiserfs/xattr.c
@@ -460,7 +460,7 @@ reiserfs_xattr_set(struct inode *inode, 
 	newattrs.ia_size = buffer_size;
 	newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
 	mutex_lock_nested(&xinode->i_mutex, I_MUTEX_XATTR);
-	err = notify_change(dentry, &newattrs);
+	err = notify_change(dentry, NULL, &newattrs);
 	if (err)
 		goto out_filp;
 
@@ -791,7 +791,7 @@ reiserfs_chown_xattrs_filler(void *buf, 
 	}
 
 	if (!S_ISDIR(xafile->d_inode->i_mode))
-		err = notify_change(xafile, attrs);
+	    err = notify_change(xafile, NULL, attrs);
 	dput(xafile);
 
 	return err;
@@ -835,7 +835,7 @@ int reiserfs_chown_xattrs(struct inode *
 		goto out_dir;
 	}
 
-	err = notify_change(dir, attrs);
+	err = notify_change(dir, NULL, attrs);
 	unlock_kernel();
 
       out_dir:
--- a/fs/sysfs/file.c
+++ b/fs/sysfs/file.c
@@ -582,7 +582,7 @@ int sysfs_chmod_file(struct kobject *kob
 
 	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
 	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
-	rc = notify_change(victim, &newattrs);
+	rc = notify_change(victim, NULL, &newattrs);
 
 	if (rc == 0) {
 		mutex_lock(&sysfs_mutex);
--- a/fs/unionfs/copyup.c
+++ b/fs/unionfs/copyup.c
@@ -121,7 +121,8 @@ out:
  */
 static int copyup_permissions(struct super_block *sb,
 			      struct dentry *old_lower_dentry,
-			      struct dentry *new_lower_dentry)
+			      struct dentry *new_lower_dentry,
+			      struct vfsmount *new_lower_mnt)
 {
 	struct inode *i = old_lower_dentry->d_inode;
 	struct iattr newattrs;
@@ -135,14 +136,14 @@ static int copyup_permissions(struct sup
 	newattrs.ia_valid = ATTR_CTIME | ATTR_ATIME | ATTR_MTIME |
 		ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_FORCE |
 		ATTR_GID | ATTR_UID;
-	err = notify_change(new_lower_dentry, &newattrs);
+	err = notify_change(new_lower_dentry, new_lower_mnt, &newattrs);
 	if (err)
 		goto out;
 
 	/* now try to change the mode and ignore EOPNOTSUPP on symlinks */
 	newattrs.ia_mode = i->i_mode;
 	newattrs.ia_valid = ATTR_MODE | ATTR_FORCE;
-	err = notify_change(new_lower_dentry, &newattrs);
+	err = notify_change(new_lower_dentry, new_lower_mnt, &newattrs);
 	if (err == -EOPNOTSUPP &&
 	    S_ISLNK(new_lower_dentry->d_inode->i_mode)) {
 		printk(KERN_WARNING
@@ -373,6 +374,7 @@ int copyup_dentry(struct inode *dir, str
 		  struct file **copyup_file, loff_t len)
 {
 	struct dentry *new_lower_dentry;
+	struct vfsmount *new_lower_mnt;
 	struct dentry *old_lower_dentry = NULL;
 	struct super_block *sb;
 	int err = 0;
@@ -399,7 +401,8 @@ int copyup_dentry(struct inode *dir, str
 		goto out;
 
 	/* Create the directory structure above this dentry. */
-	new_lower_dentry = create_parents(dir, dentry, name, new_bindex);
+	new_lower_dentry = create_parents(dir, dentry, name, new_bindex,
+					  &new_lower_mnt);
 	if (IS_ERR(new_lower_dentry)) {
 		err = PTR_ERR(new_lower_dentry);
 		goto out;
@@ -460,7 +463,8 @@ int copyup_dentry(struct inode *dir, str
 		goto out_unlink;
 
 	/* Set permissions. */
-	err = copyup_permissions(sb, old_lower_dentry, new_lower_dentry);
+	err = copyup_permissions(sb, old_lower_dentry, new_lower_dentry,
+				 new_lower_mnt);
 	if (err)
 		goto out_unlink;
 
@@ -663,13 +667,15 @@ static void __set_dentry(struct dentry *
  * in the bindex branch.
  */
 struct dentry *create_parents(struct inode *dir, struct dentry *dentry,
-			      const char *name, int bindex)
+			      const char *name, int bindex,
+			      struct vfsmount **mnt)
 {
 	int err;
 	struct dentry *child_dentry;
 	struct dentry *parent_dentry;
 	struct dentry *lower_parent_dentry = NULL;
 	struct dentry *lower_dentry = NULL;
+	struct vfsmount *lower_mnt = NULL;
 	const char *childname;
 	unsigned int childnamelen;
 	int nr_dentry;
@@ -720,6 +726,7 @@ struct dentry *create_parents(struct ino
 		/* find out the lower_parent_dentry in the given branch */
 		lower_parent_dentry =
 			unionfs_lower_dentry_idx(parent_dentry, bindex);
+		lower_mnt = unionfs_lower_mnt_idx(parent_dentry, bindex);
 
 		/* grow path table */
 		if (count == nr_dentry) {
@@ -802,7 +809,7 @@ begin:
 
 		if (!err)
 			err = copyup_permissions(dir->i_sb, child_dentry,
-						 lower_dentry);
+						 lower_dentry, lower_mnt);
 		unlock_dir(lower_parent_dentry);
 		if (err) {
 			struct inode *inode = lower_dentry->d_inode;
@@ -843,6 +850,7 @@ out:
 		while (count)
 			unionfs_unlock_dentry(path[count--]);
 	kfree(path);
+	*mnt = lower_mnt;
 	return lower_dentry;
 }
 
--- a/fs/unionfs/inode.c
+++ b/fs/unionfs/inode.c
@@ -23,6 +23,7 @@ static int unionfs_create(struct inode *
 {
 	int err = 0;
 	struct dentry *lower_dentry = NULL;
+	struct vfsmount *lower_mnt = NULL;
 	struct dentry *wh_dentry = NULL;
 	struct dentry *lower_parent_dentry = NULL;
 	char *name = NULL;
@@ -119,7 +120,8 @@ static int unionfs_create(struct inode *
 		 * dentry directory structure in branch 0.
 		 */
 		lower_dentry = create_parents(parent, dentry,
-					      dentry->d_name.name, 0);
+					      dentry->d_name.name, 0,
+					      &lower_mnt);
 		if (IS_ERR(lower_dentry)) {
 			err = PTR_ERR(lower_dentry);
 			goto out;
@@ -221,6 +223,7 @@ static int unionfs_link(struct dentry *o
 	int err = 0;
 	struct dentry *lower_old_dentry = NULL;
 	struct dentry *lower_new_dentry = NULL;
+	struct vfsmount *lower_new_mnt = NULL;
 	struct dentry *lower_dir_dentry = NULL;
 	struct dentry *whiteout_dentry;
 	char *name = NULL;
@@ -281,7 +284,8 @@ static int unionfs_link(struct dentry *o
 	if (dbstart(old_dentry) != dbstart(new_dentry)) {
 		lower_new_dentry = create_parents(dir, new_dentry,
 						  new_dentry->d_name.name,
-						  dbstart(old_dentry));
+						  dbstart(old_dentry),
+						  &lower_new_mnt);
 		err = PTR_ERR(lower_new_dentry);
 		if (IS_COPYUP_ERR(err))
 			goto docopyup;
@@ -314,7 +318,7 @@ docopyup:
 				lower_new_dentry =
 					create_parents(dir, new_dentry,
 						       new_dentry->d_name.name,
-						       bindex);
+						       bindex, &lower_new_mnt);
 				lower_old_dentry =
 					unionfs_lower_dentry(old_dentry);
 				lower_dir_dentry =
@@ -369,6 +373,7 @@ static int unionfs_symlink(struct inode 
 {
 	int err = 0;
 	struct dentry *lower_dentry = NULL;
+	struct vfsmount *lower_mnt = NULL;
 	struct dentry *whiteout_dentry = NULL;
 	struct dentry *lower_dir_dentry = NULL;
 	umode_t mode;
@@ -458,7 +463,7 @@ static int unionfs_symlink(struct inode 
 			 */
 			lower_dentry = create_parents(dir, dentry,
 						      dentry->d_name.name,
-						      bindex);
+						      bindex, &lower_mnt);
 			if (!lower_dentry || IS_ERR(lower_dentry)) {
 				if (IS_ERR(lower_dentry))
 					err = PTR_ERR(lower_dentry);
@@ -532,6 +537,7 @@ static int unionfs_mkdir(struct inode *p
 {
 	int err = 0;
 	struct dentry *lower_dentry = NULL, *whiteout_dentry = NULL;
+	struct vfsmount *lower_mnt = NULL;
 	struct dentry *lower_parent_dentry = NULL;
 	int bindex = 0, bstart;
 	char *name = NULL;
@@ -607,7 +613,7 @@ static int unionfs_mkdir(struct inode *p
 		if (!lower_dentry) {
 			lower_dentry = create_parents(parent, dentry,
 						      dentry->d_name.name,
-						      bindex);
+						      bindex, &lower_mnt);
 			if (!lower_dentry || IS_ERR(lower_dentry)) {
 				printk(KERN_ERR "unionfs: lower dentry "
 				       " NULL for bindex = %d\n", bindex);
@@ -687,6 +693,7 @@ static int unionfs_mknod(struct inode *d
 {
 	int err = 0;
 	struct dentry *lower_dentry = NULL, *whiteout_dentry = NULL;
+	struct vfsmount *lower_mnt = NULL;
 	struct dentry *lower_parent_dentry = NULL;
 	int bindex = 0, bstart;
 	char *name = NULL;
@@ -755,7 +762,7 @@ static int unionfs_mknod(struct inode *d
 		if (!lower_dentry) {
 			lower_dentry = create_parents(dir, dentry,
 						      dentry->d_name.name,
-						      bindex);
+						      bindex, &lower_mnt);
 			if (IS_ERR(lower_dentry)) {
 				printk(KERN_ERR "unionfs: failed to create "
 				       "parents on %d, err = %ld\n",
@@ -1004,6 +1011,7 @@ static int unionfs_setattr(struct dentry
 {
 	int err = 0;
 	struct dentry *lower_dentry;
+	struct vfsmount *lower_mnt;
 	struct inode *inode = NULL;
 	struct inode *lower_inode = NULL;
 	int bstart, bend, bindex;
@@ -1035,6 +1043,7 @@ static int unionfs_setattr(struct dentry
 		if (!lower_dentry)
 			continue;
 		BUG_ON(lower_dentry->d_inode == NULL);
+		lower_mnt = unionfs_lower_mnt_idx(dentry, bindex);
 
 		/* If the file is on a read only branch */
 		if (is_robranch_super(dentry->d_sb, bindex)
@@ -1067,7 +1076,7 @@ static int unionfs_setattr(struct dentry
 			}
 
 		}
-		err = notify_change(lower_dentry, ia);
+		err = notify_change(lower_dentry, lower_mnt, ia);
 		if (err)
 			goto out;
 		break;
--- a/fs/unionfs/rename.c
+++ b/fs/unionfs/rename.c
@@ -25,6 +25,7 @@ static int __unionfs_rename(struct inode
 	int err = 0;
 	struct dentry *lower_old_dentry;
 	struct dentry *lower_new_dentry;
+	struct vfsmount *lower_new_mnt;
 	struct dentry *lower_old_dir_dentry;
 	struct dentry *lower_new_dir_dentry;
 	struct dentry *lower_wh_dentry;
@@ -38,7 +39,7 @@ static int __unionfs_rename(struct inode
 		lower_new_dentry =
 			create_parents(new_dentry->d_parent->d_inode,
 				       new_dentry, new_dentry->d_name.name,
-				       bindex);
+				       bindex, &lower_new_mnt);
 		if (IS_ERR(lower_new_dentry)) {
 			err = PTR_ERR(lower_new_dentry);
 			if (IS_COPYUP_ERR(err))
--- a/fs/unionfs/subr.c
+++ b/fs/unionfs/subr.c
@@ -28,6 +28,7 @@ int create_whiteout(struct dentry *dentr
 	int bstart, bend, bindex;
 	struct dentry *lower_dir_dentry;
 	struct dentry *lower_dentry;
+	struct vfsmount *lower_mnt;
 	struct dentry *lower_wh_dentry;
 	struct nameidata nd;
 	char *name = NULL;
@@ -59,7 +60,7 @@ int create_whiteout(struct dentry *dentr
 			lower_dentry = create_parents(dentry->d_inode,
 						      dentry,
 						      dentry->d_name.name,
-						      bindex);
+						      bindex, &lower_mnt);
 			if (!lower_dentry || IS_ERR(lower_dentry)) {
 				int ret = PTR_ERR(lower_dentry);
 				if (!IS_COPYUP_ERR(ret))
--- a/fs/unionfs/union.h
+++ b/fs/unionfs/union.h
@@ -292,7 +292,8 @@ extern void release_lower_nd(struct name
 
 /* replicates the directory structure up to given dentry in given branch */
 extern struct dentry *create_parents(struct inode *dir, struct dentry *dentry,
-				     const char *name, int bindex);
+				     const char *name, int bindex,
+				     struct vfsmount **mnt);
 extern int make_dir_opaque(struct dentry *dir, int bindex);
 
 /* partial lookup */
--- a/fs/utimes.c
+++ b/fs/utimes.c
@@ -56,11 +56,10 @@ long do_utimes(int dfd, char __user *fil
 {
 	int error;
 	struct nameidata nd;
-	struct dentry *dentry;
+	struct path path;
 	struct inode *inode;
 	struct iattr newattrs;
 	struct file *f = NULL;
-	struct vfsmount *mnt;
 
 	error = -EINVAL;
 	if (times && (!nsec_valid(times[0].tv_nsec) ||
@@ -80,20 +79,20 @@ long do_utimes(int dfd, char __user *fil
 		f = fget(dfd);
 		if (!f)
 			goto out;
-		dentry = f->f_path.dentry;
-		mnt = f->f_path.mnt;
+
+		path = f->f_path;
 	} else {
 		error = __user_walk_fd(dfd, filename, (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW, &nd);
 		if (error)
 			goto out;
 
-		dentry = nd.path.dentry;
-		mnt = nd.path.mnt;
+		path.dentry = nd.path.dentry;
+		path.mnt = nd.path.mnt;
 	}
 
-	inode = dentry->d_inode;
+	inode = path.dentry->d_inode;
 
-	error = mnt_want_write(mnt);
+	error = mnt_want_write(path.mnt);
 	if (error)
 		goto dput_and_out;
 
@@ -136,10 +135,10 @@ long do_utimes(int dfd, char __user *fil
 		}
 	}
 	mutex_lock(&inode->i_mutex);
-	error = notify_change(dentry, &newattrs);
+	error = notify_change(path.dentry, path.mnt, &newattrs);
 	mutex_unlock(&inode->i_mutex);
 mnt_drop_write_and_out:
-	mnt_drop_write(mnt);
+	mnt_drop_write(path.mnt);
 dput_and_out:
 	if (f)
 		fput(f);
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1538,8 +1538,8 @@ static inline int break_lease(struct ino
 
 /* fs/open.c */
 
-extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs,
-		       struct file *filp);
+extern int do_truncate(struct dentry *, struct vfsmount *, loff_t start,
+		       unsigned int time_attrs, struct file *filp);
 extern long do_sys_open(int dfd, const char __user *filename, int flags,
 			int mode);
 extern struct file * dentry_open(struct dentry *, struct vfsmount *, int);
@@ -1695,7 +1695,7 @@ extern int do_remount_sb(struct super_bl
 #ifdef CONFIG_BLOCK
 extern sector_t bmap(struct inode *, sector_t);
 #endif
-extern int notify_change(struct dentry *, struct iattr *);
+extern int notify_change(struct dentry *, struct vfsmount *, struct iattr *);
 extern int permission(struct inode *, int, struct nameidata *);
 extern int generic_permission(struct inode *, int,
 		int (*check_acl)(struct inode *, int));
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -1643,7 +1643,7 @@ int __remove_suid(struct path *path, int
 	struct iattr newattrs;
 
 	newattrs.ia_valid = ATTR_FORCE | kill;
-	return notify_change(path->dentry, &newattrs);
+	return notify_change(path->dentry, path->mnt, &newattrs);
 }
 
 int remove_suid(struct path *path)
--- a/mm/tiny-shmem.c
+++ b/mm/tiny-shmem.c
@@ -81,7 +81,7 @@ struct file *shmem_file_setup(char *name
 	inode->i_nlink = 0;	/* It is unlinked */
 
 	/* notify everyone as to the change of file size */
-	error = do_truncate(dentry, size, 0, file);
+	error = do_truncate(dentry, file->f_path.mnt, size, 0, file);
 	if (error < 0)
 		goto close_file;
 

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 04/47] Pass struct vfsmount to the inode_setattr LSM hook
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (2 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 03/47] Add a vfsmount parameter to notify_change() John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 05/47] Add struct vfsmount parameter to vfs_mkdir() John, Johansen
                   ` (42 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Tony Jones,
	Andreas Gruenbacher, John Johansen

[-- Attachment #1: security-setattr.diff --]
[-- Type: text/plain, Size: 4515 bytes --]

This is needed for computing pathnames in the AppArmor LSM.

Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/attr.c                |    4 ++--
 include/linux/security.h |    8 ++++++--
 security/dummy.c         |    3 ++-
 security/security.c      |    5 +++--
 security/selinux/hooks.c |    5 +++--
 5 files changed, 16 insertions(+), 9 deletions(-)

--- a/fs/attr.c
+++ b/fs/attr.c
@@ -159,13 +159,13 @@ int notify_change(struct dentry *dentry,
 		down_write(&dentry->d_inode->i_alloc_sem);
 
 	if (inode->i_op && inode->i_op->setattr) {
-		error = security_inode_setattr(dentry, attr);
+		error = security_inode_setattr(dentry, mnt, attr);
 		if (!error)
 			error = inode->i_op->setattr(dentry, attr);
 	} else {
 		error = inode_change_ok(inode, attr);
 		if (!error)
-			error = security_inode_setattr(dentry, attr);
+			error = security_inode_setattr(dentry, mnt, attr);
 		if (!error) {
 			if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) ||
 			    (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid))
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -389,6 +389,7 @@ struct request_sock;
  *	file attributes change (such as when a file is truncated, chown/chmod
  *	operations, transferring disk quotas, etc).
  *	@dentry contains the dentry structure for the file.
+ *	@mnt is the vfsmount corresponding to @dentry (may be NULL).
  *	@attr is the iattr structure containing the new file attributes.
  *	Return 0 if permission is granted.
  * @inode_getattr:
@@ -1289,7 +1290,8 @@ struct security_operations {
 	int (*inode_readlink) (struct dentry *dentry);
 	int (*inode_follow_link) (struct dentry *dentry, struct nameidata *nd);
 	int (*inode_permission) (struct inode *inode, int mask, struct nameidata *nd);
-	int (*inode_setattr)	(struct dentry *dentry, struct iattr *attr);
+	int (*inode_setattr) (struct dentry *dentry, struct vfsmount *mnt,
+			      struct iattr *attr);
 	int (*inode_getattr) (struct vfsmount *mnt, struct dentry *dentry);
         void (*inode_delete) (struct inode *inode);
 	int (*inode_setxattr) (struct dentry *dentry, char *name, void *value,
@@ -1552,7 +1554,8 @@ int security_inode_rename(struct inode *
 int security_inode_readlink(struct dentry *dentry);
 int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd);
 int security_inode_permission(struct inode *inode, int mask, struct nameidata *nd);
-int security_inode_setattr(struct dentry *dentry, struct iattr *attr);
+int security_inode_setattr(struct dentry *dentry, struct vfsmount *mnt,
+			   struct iattr *attr);
 int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry);
 void security_inode_delete(struct inode *inode);
 int security_inode_setxattr(struct dentry *dentry, char *name,
@@ -1921,6 +1924,7 @@ static inline int security_inode_permiss
 }
 
 static inline int security_inode_setattr (struct dentry *dentry,
+					  struct vfsmount *mnt,
 					  struct iattr *attr)
 {
 	return 0;
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -353,7 +353,8 @@ static int dummy_inode_permission (struc
 	return 0;
 }
 
-static int dummy_inode_setattr (struct dentry *dentry, struct iattr *iattr)
+static int dummy_inode_setattr (struct dentry *dentry, struct vfsmount *mnt,
+				struct iattr *iattr)
 {
 	return 0;
 }
--- a/security/security.c
+++ b/security/security.c
@@ -431,11 +431,12 @@ int security_inode_permission(struct ino
 	return security_ops->inode_permission(inode, mask, nd);
 }
 
-int security_inode_setattr(struct dentry *dentry, struct iattr *attr)
+int security_inode_setattr(struct dentry *dentry, struct vfsmount *mnt,
+			   struct iattr *attr)
 {
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 		return 0;
-	return security_ops->inode_setattr(dentry, attr);
+	return security_ops->inode_setattr(dentry, mnt, attr);
 }
 
 int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2502,11 +2502,12 @@ static int selinux_inode_permission(stru
 			       file_mask_to_av(inode->i_mode, mask), NULL);
 }
 
-static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
+static int selinux_inode_setattr(struct dentry *dentry, struct vfsmount *mnt,
+				 struct iattr *iattr)
 {
 	int rc;
 
-	rc = secondary_ops->inode_setattr(dentry, iattr);
+	rc = secondary_ops->inode_setattr(dentry, mnt, iattr);
 	if (rc)
 		return rc;
 

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 05/47] Add struct vfsmount parameter to vfs_mkdir()
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (3 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 04/47] Pass struct vfsmount to the inode_setattr LSM hook John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 06/47] Pass struct vfsmount to the inode_mkdir LSM hook John, Johansen
                   ` (41 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Tony Jones,
	Andreas Gruenbacher, John Johansen

[-- Attachment #1: vfs-mkdir.diff --]
[-- Type: text/plain, Size: 6546 bytes --]

The vfsmount will be passed down to the LSM hook so that LSMs can compute
pathnames.

Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/ecryptfs/inode.c   |    5 ++++-
 fs/namei.c            |    5 +++--
 fs/nfsd/nfs4recover.c |    3 ++-
 fs/nfsd/vfs.c         |    9 ++++++---
 fs/unionfs/copyup.c   |    5 ++++-
 fs/unionfs/inode.c    |    3 ++-
 fs/unionfs/sioq.c     |    2 +-
 fs/unionfs/sioq.h     |    1 +
 include/linux/fs.h    |    2 +-
 9 files changed, 24 insertions(+), 11 deletions(-)

--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -499,11 +499,14 @@ static int ecryptfs_mkdir(struct inode *
 {
 	int rc;
 	struct dentry *lower_dentry;
+	struct vfsmount *lower_mnt;
 	struct dentry *lower_dir_dentry;
 
 	lower_dentry = ecryptfs_dentry_to_lower(dentry);
+	lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry);
 	lower_dir_dentry = lock_parent(lower_dentry);
-	rc = vfs_mkdir(lower_dir_dentry->d_inode, lower_dentry, mode);
+	rc = vfs_mkdir(lower_dir_dentry->d_inode, lower_dentry, lower_mnt,
+		       mode);
 	if (rc || !lower_dentry->d_inode)
 		goto out;
 	rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0);
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2107,7 +2107,8 @@ asmlinkage long sys_mknod(const char __u
 	return sys_mknodat(AT_FDCWD, filename, mode, dev);
 }
 
-int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+int vfs_mkdir(struct inode *dir, struct dentry *dentry, struct vfsmount *mnt,
+	      int mode)
 {
 	int error = may_create(dir, dentry, NULL);
 
@@ -2154,7 +2155,7 @@ asmlinkage long sys_mkdirat(int dfd, con
 	error = mnt_want_write(nd.path.mnt);
 	if (error)
 		goto out_dput;
-	error = vfs_mkdir(nd.path.dentry->d_inode, dentry, mode);
+	error = vfs_mkdir(nd.path.dentry->d_inode, dentry, nd.path.mnt, mode);
 	mnt_drop_write(nd.path.mnt);
 out_dput:
 	dput(dentry);
--- a/fs/nfsd/nfs4recover.c
+++ b/fs/nfsd/nfs4recover.c
@@ -158,7 +158,8 @@ nfsd4_create_clid_dir(struct nfs4_client
 	status = mnt_want_write(rec_dir.path.mnt);
 	if (status)
 		goto out_put;
-	status = vfs_mkdir(rec_dir.path.dentry->d_inode, dentry, S_IRWXU);
+	status = vfs_mkdir(rec_dir.path.dentry->d_inode, dentry,
+			   rec_dir.path.mnt, S_IRWXU);
 	mnt_drop_write(rec_dir.path.mnt);
 out_put:
 	dput(dentry);
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1166,6 +1166,7 @@ nfsd_create(struct svc_rqst *rqstp, stru
 		int type, dev_t rdev, struct svc_fh *resfhp)
 {
 	struct dentry	*dentry, *dchild = NULL;
+	struct svc_export *exp;
 	struct inode	*dirp;
 	__be32		err;
 	int		host_err;
@@ -1182,6 +1183,7 @@ nfsd_create(struct svc_rqst *rqstp, stru
 		goto out;
 
 	dentry = fhp->fh_dentry;
+	exp = fhp->fh_export;
 	dirp = dentry->d_inode;
 
 	err = nfserr_notdir;
@@ -1198,7 +1200,7 @@ nfsd_create(struct svc_rqst *rqstp, stru
 		host_err = PTR_ERR(dchild);
 		if (IS_ERR(dchild))
 			goto out_nfserr;
-		err = fh_compose(resfhp, fhp->fh_export, dchild, fhp);
+		err = fh_compose(resfhp, exp, dchild, fhp);
 		if (err)
 			goto out;
 	} else {
@@ -1237,7 +1239,8 @@ nfsd_create(struct svc_rqst *rqstp, stru
 		host_err = vfs_create(dirp, dchild, iap->ia_mode, NULL);
 		break;
 	case S_IFDIR:
-		host_err = vfs_mkdir(dirp, dchild, iap->ia_mode);
+		host_err = vfs_mkdir(dirp, dchild, exp->ex_path.mnt,
+				     iap->ia_mode);
 		break;
 	case S_IFCHR:
 	case S_IFBLK:
@@ -1256,7 +1259,7 @@ nfsd_create(struct svc_rqst *rqstp, stru
 	if (host_err < 0)
 		goto out_nfserr;
 
-	if (EX_ISSYNC(fhp->fh_export)) {
+	if (EX_ISSYNC(exp)) {
 		err = nfserrno(nfsd_sync_dir(dentry));
 		write_inode_now(dchild->d_inode, 1);
 	}
--- a/fs/unionfs/copyup.c
+++ b/fs/unionfs/copyup.c
@@ -165,6 +165,7 @@ out:
  */
 static int __copyup_ndentry(struct dentry *old_lower_dentry,
 			    struct dentry *new_lower_dentry,
+			    struct vfsmount *new_lower_mnt,
 			    struct dentry *new_lower_parent_dentry,
 			    char *symbuf)
 {
@@ -175,6 +176,7 @@ static int __copyup_ndentry(struct dentr
 	if (S_ISDIR(old_mode)) {
 		args.mkdir.parent = new_lower_parent_dentry->d_inode;
 		args.mkdir.dentry = new_lower_dentry;
+		args.mkdir.mnt = new_lower_mnt;
 		args.mkdir.mode = old_mode;
 
 		run_sioq(__unionfs_mkdir, &args);
@@ -445,7 +447,7 @@ int copyup_dentry(struct inode *dir, str
 
 	/* create the new inode */
 	err = __copyup_ndentry(old_lower_dentry, new_lower_dentry,
-			       new_lower_parent_dentry, symbuf);
+			       new_lower_mnt, new_lower_parent_dentry, symbuf);
 
 	if (err) {
 		__clear(dentry, old_lower_dentry,
@@ -802,6 +804,7 @@ begin:
 
 		args.mkdir.parent = lower_parent_dentry->d_inode;
 		args.mkdir.dentry = lower_dentry;
+		args.mkdir.mnt = lower_mnt;
 		args.mkdir.mode = child_dentry->d_inode->i_mode;
 
 		run_sioq(__unionfs_mkdir, &args);
--- a/fs/unionfs/inode.c
+++ b/fs/unionfs/inode.c
@@ -610,6 +610,7 @@ static int unionfs_mkdir(struct inode *p
 			continue;
 
 		lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+		lower_mnt = unionfs_lower_mnt_idx(dentry, bindex);
 		if (!lower_dentry) {
 			lower_dentry = create_parents(parent, dentry,
 						      dentry->d_name.name,
@@ -629,7 +630,7 @@ static int unionfs_mkdir(struct inode *p
 		}
 
 		err = vfs_mkdir(lower_parent_dentry->d_inode, lower_dentry,
-				mode);
+				lower_mnt, mode);
 
 		unlock_dir(lower_parent_dentry);
 
--- a/fs/unionfs/sioq.c
+++ b/fs/unionfs/sioq.c
@@ -69,7 +69,7 @@ void __unionfs_mkdir(struct work_struct 
 	struct sioq_args *args = container_of(work, struct sioq_args, work);
 	struct mkdir_args *m = &args->mkdir;
 
-	args->err = vfs_mkdir(m->parent, m->dentry, m->mode);
+	args->err = vfs_mkdir(m->parent, m->dentry, m->mnt, m->mode);
 	complete(&args->comp);
 }
 
--- a/fs/unionfs/sioq.h
+++ b/fs/unionfs/sioq.h
@@ -35,6 +35,7 @@ struct create_args {
 struct mkdir_args {
 	struct inode *parent;
 	struct dentry *dentry;
+	struct vfsmount *mnt;
 	umode_t mode;
 };
 
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1070,7 +1070,7 @@ extern void unlock_super(struct super_bl
  */
 extern int vfs_permission(struct nameidata *, int);
 extern int vfs_create(struct inode *, struct dentry *, int, struct nameidata *);
-extern int vfs_mkdir(struct inode *, struct dentry *, int);
+extern int vfs_mkdir(struct inode *, struct dentry *, struct vfsmount *, int);
 extern int vfs_mknod(struct inode *, struct dentry *, int, dev_t);
 extern int vfs_symlink(struct inode *, struct dentry *, const char *, int);
 extern int vfs_link(struct dentry *, struct inode *, struct dentry *);

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 06/47] Pass struct vfsmount to the inode_mkdir LSM hook
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (4 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 05/47] Add struct vfsmount parameter to vfs_mkdir() John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 07/47] Add a struct vfsmount parameter to vfs_mknod() John, Johansen
                   ` (40 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Tony Jones,
	Andreas Gruenbacher, John Johansen

[-- Attachment #1: security-mkdir.diff --]
[-- Type: text/plain, Size: 3955 bytes --]

This is needed for computing pathnames in the AppArmor LSM.

Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/namei.c               |    2 +-
 include/linux/security.h |    8 ++++++--
 security/dummy.c         |    2 +-
 security/security.c      |    5 +++--
 security/selinux/hooks.c |    3 ++-
 5 files changed, 13 insertions(+), 7 deletions(-)

--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2119,7 +2119,7 @@ int vfs_mkdir(struct inode *dir, struct 
 		return -EPERM;
 
 	mode &= (S_IRWXUGO|S_ISVTX);
-	error = security_inode_mkdir(dir, dentry, mode);
+	error = security_inode_mkdir(dir, dentry, mnt, mode);
 	if (error)
 		return error;
 
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -339,6 +339,7 @@ struct request_sock;
  *	associated with inode strcture @dir. 
  *	@dir containst the inode structure of parent of the directory to be created.
  *	@dentry contains the dentry structure of new directory.
+ *	@mnt is the vfsmount corresponding to @dentry (may be NULL).
  *	@mode contains the mode of new directory.
  *	Return 0 if permission is granted.
  * @inode_rmdir:
@@ -1281,7 +1282,8 @@ struct security_operations {
 	int (*inode_unlink) (struct inode *dir, struct dentry *dentry);
 	int (*inode_symlink) (struct inode *dir,
 	                      struct dentry *dentry, const char *old_name);
-	int (*inode_mkdir) (struct inode *dir, struct dentry *dentry, int mode);
+	int (*inode_mkdir) (struct inode *dir, struct dentry *dentry,
+			    struct vfsmount *mnt, int mode);
 	int (*inode_rmdir) (struct inode *dir, struct dentry *dentry);
 	int (*inode_mknod) (struct inode *dir, struct dentry *dentry,
 	                    int mode, dev_t dev);
@@ -1546,7 +1548,8 @@ int security_inode_link(struct dentry *o
 int security_inode_unlink(struct inode *dir, struct dentry *dentry);
 int security_inode_symlink(struct inode *dir, struct dentry *dentry,
 			    const char *old_name);
-int security_inode_mkdir(struct inode *dir, struct dentry *dentry, int mode);
+int security_inode_mkdir(struct inode *dir, struct dentry *dentry,
+			 struct vfsmount *mnt, int mode);
 int security_inode_rmdir(struct inode *dir, struct dentry *dentry);
 int security_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev);
 int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
@@ -1880,6 +1883,7 @@ static inline int security_inode_symlink
 
 static inline int security_inode_mkdir (struct inode *dir,
 					struct dentry *dentry,
+					struct vfsmount *mnt,
 					int mode)
 {
 	return 0;
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -313,7 +313,7 @@ static int dummy_inode_symlink (struct i
 }
 
 static int dummy_inode_mkdir (struct inode *inode, struct dentry *dentry,
-			      int mask)
+			      struct vfsmount *mnt, int mask)
 {
 	return 0;
 }
--- a/security/security.c
+++ b/security/security.c
@@ -379,11 +379,12 @@ int security_inode_symlink(struct inode 
 	return security_ops->inode_symlink(dir, dentry, old_name);
 }
 
-int security_inode_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+int security_inode_mkdir(struct inode *dir, struct dentry *dentry,
+			 struct vfsmount *mnt, int mode)
 {
 	if (unlikely(IS_PRIVATE(dir)))
 		return 0;
-	return security_ops->inode_mkdir(dir, dentry, mode);
+	return security_ops->inode_mkdir(dir, dentry, mnt, mode);
 }
 
 int security_inode_rmdir(struct inode *dir, struct dentry *dentry)
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2442,7 +2442,8 @@ static int selinux_inode_symlink(struct 
 	return may_create(dir, dentry, SECCLASS_LNK_FILE);
 }
 
-static int selinux_inode_mkdir(struct inode *dir, struct dentry *dentry, int mask)
+static int selinux_inode_mkdir(struct inode *dir, struct dentry *dentry,
+			       struct vfsmount *mnt, int mask)
 {
 	return may_create(dir, dentry, SECCLASS_DIR);
 }

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 07/47] Add a struct vfsmount parameter to vfs_mknod()
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (5 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 06/47] Pass struct vfsmount to the inode_mkdir LSM hook John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 08/47] Pass struct vfsmount to the inode_mknod LSM hook John, Johansen
                   ` (39 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Tony Jones,
	Andreas Gruenbacher, John Johansen

[-- Attachment #1: vfs-mknod.diff --]
[-- Type: text/plain, Size: 5152 bytes --]

The vfsmount will be passed down to the LSM hook so that LSMs can compute
pathnames.

Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/ecryptfs/inode.c |    5 ++++-
 fs/namei.c          |   10 ++++++----
 fs/nfsd/vfs.c       |    3 ++-
 fs/unionfs/copyup.c |    1 +
 fs/unionfs/inode.c  |    3 ++-
 fs/unionfs/sioq.c   |    2 +-
 fs/unionfs/sioq.h   |    1 +
 include/linux/fs.h  |    2 +-
 net/unix/af_unix.c  |    3 ++-
 9 files changed, 20 insertions(+), 10 deletions(-)

--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -550,11 +550,14 @@ ecryptfs_mknod(struct inode *dir, struct
 {
 	int rc;
 	struct dentry *lower_dentry;
+	struct vfsmount *lower_mnt;
 	struct dentry *lower_dir_dentry;
 
 	lower_dentry = ecryptfs_dentry_to_lower(dentry);
+	lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry);
 	lower_dir_dentry = lock_parent(lower_dentry);
-	rc = vfs_mknod(lower_dir_dentry->d_inode, lower_dentry, mode, dev);
+	rc = vfs_mknod(lower_dir_dentry->d_inode, lower_dentry, lower_mnt, mode,
+		       dev);
 	if (rc || !lower_dentry->d_inode)
 		goto out;
 	rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0);
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2005,7 +2005,8 @@ fail:
 }
 EXPORT_SYMBOL_GPL(lookup_create);
 
-int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
+int vfs_mknod(struct inode *dir, struct dentry *dentry, struct vfsmount *mnt,
+	      int mode, dev_t dev)
 {
 	int error = may_create(dir, dentry, NULL);
 
@@ -2082,12 +2083,13 @@ asmlinkage long sys_mknodat(int dfd, con
 						mode, &nd);
 			break;
 		case S_IFCHR: case S_IFBLK:
-			error = vfs_mknod(nd.path.dentry->d_inode, dentry, mode,
-					new_decode_dev(dev));
+			error = vfs_mknod(nd.path.dentry->d_inode, dentry,
+					  nd.path.mnt, mode,
+					  new_decode_dev(dev));
 			break;
 		case S_IFIFO: case S_IFSOCK:
 			error = vfs_mknod(nd.path.dentry->d_inode, dentry,
-						mode, 0);
+					  nd.path.mnt, mode, 0);
 			break;
 	}
 	mnt_drop_write(nd.path.mnt);
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1249,7 +1249,8 @@ nfsd_create(struct svc_rqst *rqstp, stru
 		host_err = mnt_want_write(fhp->fh_export->ex_path.mnt);
 		if (host_err)
 			break;
-		host_err = vfs_mknod(dirp, dchild, iap->ia_mode, rdev);
+		host_err = vfs_mknod(dirp, dchild, exp->ex_path.mnt,
+				     iap->ia_mode, rdev);
 		mnt_drop_write(fhp->fh_export->ex_path.mnt);
 		break;
 	default:
--- a/fs/unionfs/copyup.c
+++ b/fs/unionfs/copyup.c
@@ -193,6 +193,7 @@ static int __copyup_ndentry(struct dentr
 		   S_ISFIFO(old_mode) || S_ISSOCK(old_mode)) {
 		args.mknod.parent = new_lower_parent_dentry->d_inode;
 		args.mknod.dentry = new_lower_dentry;
+		args.mknod.mnt = new_lower_mnt;
 		args.mknod.mode = old_mode;
 		args.mknod.dev = old_lower_dentry->d_inode->i_rdev;
 
--- a/fs/unionfs/inode.c
+++ b/fs/unionfs/inode.c
@@ -760,6 +760,7 @@ static int unionfs_mknod(struct inode *d
 			continue;
 
 		lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+		lower_mnt = unionfs_lower_mnt_idx(dentry, bindex);
 		if (!lower_dentry) {
 			lower_dentry = create_parents(dir, dentry,
 						      dentry->d_name.name,
@@ -779,7 +780,7 @@ static int unionfs_mknod(struct inode *d
 		}
 
 		err = vfs_mknod(lower_parent_dentry->d_inode,
-				lower_dentry, mode, dev);
+				lower_dentry, lower_mnt, mode, dev);
 
 		if (err) {
 			unlock_dir(lower_parent_dentry);
--- a/fs/unionfs/sioq.c
+++ b/fs/unionfs/sioq.c
@@ -78,7 +78,7 @@ void __unionfs_mknod(struct work_struct 
 	struct sioq_args *args = container_of(work, struct sioq_args, work);
 	struct mknod_args *m = &args->mknod;
 
-	args->err = vfs_mknod(m->parent, m->dentry, m->mode, m->dev);
+	args->err = vfs_mknod(m->parent, m->dentry, m->mnt, m->mode, m->dev);
 	complete(&args->comp);
 }
 
--- a/fs/unionfs/sioq.h
+++ b/fs/unionfs/sioq.h
@@ -42,6 +42,7 @@ struct mkdir_args {
 struct mknod_args {
 	struct inode *parent;
 	struct dentry *dentry;
+	struct vfsmount *mnt;
 	umode_t mode;
 	dev_t dev;
 };
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1071,7 +1071,7 @@ extern void unlock_super(struct super_bl
 extern int vfs_permission(struct nameidata *, int);
 extern int vfs_create(struct inode *, struct dentry *, int, struct nameidata *);
 extern int vfs_mkdir(struct inode *, struct dentry *, struct vfsmount *, int);
-extern int vfs_mknod(struct inode *, struct dentry *, int, dev_t);
+extern int vfs_mknod(struct inode *, struct dentry *, struct vfsmount *, int, dev_t);
 extern int vfs_symlink(struct inode *, struct dentry *, const char *, int);
 extern int vfs_link(struct dentry *, struct inode *, struct dentry *);
 extern int vfs_rmdir(struct inode *, struct dentry *);
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -822,7 +822,8 @@ static int unix_bind(struct socket *sock
 		err = mnt_want_write(nd.path.mnt);
 		if (err)
 			goto out_mknod_dput;
-		err = vfs_mknod(nd.path.dentry->d_inode, dentry, mode, 0);
+		err = vfs_mknod(nd.path.dentry->d_inode, dentry, nd.path.mnt,
+				mode, 0);
 		mnt_drop_write(nd.path.mnt);
 		if (err)
 			goto out_mknod_dput;

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 08/47] Pass struct vfsmount to the inode_mknod LSM hook
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (6 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 07/47] Add a struct vfsmount parameter to vfs_mknod() John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 09/47] Add a struct vfsmount parameter to vfs_symlink() John, Johansen
                   ` (38 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Tony Jones,
	Andreas Gruenbacher, John Johansen

[-- Attachment #1: security-mknod.diff --]
[-- Type: text/plain, Size: 4050 bytes --]

This is needed for computing pathnames in the AppArmor LSM.

Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/namei.c               |    2 +-
 include/linux/security.h |    7 +++++--
 security/dummy.c         |    2 +-
 security/security.c      |    5 +++--
 security/selinux/hooks.c |    5 +++--
 5 files changed, 13 insertions(+), 8 deletions(-)

--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2019,7 +2019,7 @@ int vfs_mknod(struct inode *dir, struct 
 	if (!dir->i_op || !dir->i_op->mknod)
 		return -EPERM;
 
-	error = security_inode_mknod(dir, dentry, mode, dev);
+	error = security_inode_mknod(dir, dentry, mnt, mode, dev);
 	if (error)
 		return error;
 
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -354,6 +354,7 @@ struct request_sock;
  *	and not this hook.
  *	@dir contains the inode structure of parent of the new file.
  *	@dentry contains the dentry structure of the new file.
+ *	@mnt is the vfsmount corresponding to @dentry (may be NULL).
  *	@mode contains the mode of the new file.
  *	@dev contains the device number.
  *	Return 0 if permission is granted.
@@ -1286,7 +1287,7 @@ struct security_operations {
 			    struct vfsmount *mnt, int mode);
 	int (*inode_rmdir) (struct inode *dir, struct dentry *dentry);
 	int (*inode_mknod) (struct inode *dir, struct dentry *dentry,
-	                    int mode, dev_t dev);
+			    struct vfsmount *mnt, int mode, dev_t dev);
 	int (*inode_rename) (struct inode *old_dir, struct dentry *old_dentry,
 	                     struct inode *new_dir, struct dentry *new_dentry);
 	int (*inode_readlink) (struct dentry *dentry);
@@ -1551,7 +1552,8 @@ int security_inode_symlink(struct inode 
 int security_inode_mkdir(struct inode *dir, struct dentry *dentry,
 			 struct vfsmount *mnt, int mode);
 int security_inode_rmdir(struct inode *dir, struct dentry *dentry);
-int security_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev);
+int security_inode_mknod(struct inode *dir, struct dentry *dentry,
+			 struct vfsmount *mnt, int mode, dev_t dev);
 int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
 			   struct inode *new_dir, struct dentry *new_dentry);
 int security_inode_readlink(struct dentry *dentry);
@@ -1897,6 +1899,7 @@ static inline int security_inode_rmdir (
 
 static inline int security_inode_mknod (struct inode *dir,
 					struct dentry *dentry,
+					struct vfsmount *mnt,
 					int mode, dev_t dev)
 {
 	return 0;
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -324,7 +324,7 @@ static int dummy_inode_rmdir (struct ino
 }
 
 static int dummy_inode_mknod (struct inode *inode, struct dentry *dentry,
-			      int mode, dev_t dev)
+			      struct vfsmount *mnt, int mode, dev_t dev)
 {
 	return 0;
 }
--- a/security/security.c
+++ b/security/security.c
@@ -394,11 +394,12 @@ int security_inode_rmdir(struct inode *d
 	return security_ops->inode_rmdir(dir, dentry);
 }
 
-int security_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
+int security_inode_mknod(struct inode *dir, struct dentry *dentry,
+			 struct vfsmount *mnt, int mode, dev_t dev)
 {
 	if (unlikely(IS_PRIVATE(dir)))
 		return 0;
-	return security_ops->inode_mknod(dir, dentry, mode, dev);
+	return security_ops->inode_mknod(dir, dentry, mnt, mode, dev);
 }
 
 int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2453,11 +2453,12 @@ static int selinux_inode_rmdir(struct in
 	return may_link(dir, dentry, MAY_RMDIR);
 }
 
-static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
+static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry,
+			       struct vfsmount *mnt, int mode, dev_t dev)
 {
 	int rc;
 
-	rc = secondary_ops->inode_mknod(dir, dentry, mode, dev);
+	rc = secondary_ops->inode_mknod(dir, dentry, mnt, mode, dev);
 	if (rc)
 		return rc;
 

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 09/47] Add a struct vfsmount parameter to vfs_symlink()
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (7 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 08/47] Pass struct vfsmount to the inode_mknod LSM hook John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 10/47] Pass struct vfsmount to the inode_symlink LSM hook John, Johansen
                   ` (37 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Tony Jones,
	Andreas Gruenbacher, John Johansen

[-- Attachment #1: vfs-symlink.diff --]
[-- Type: text/plain, Size: 5931 bytes --]

The vfsmount will be passed down to the LSM hook so that LSMs can compute
pathnames.

Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/ecryptfs/inode.c |    4 +++-
 fs/namei.c          |    6 ++++--
 fs/nfsd/vfs.c       |   13 +++++++++----
 fs/unionfs/copyup.c |    1 +
 fs/unionfs/inode.c  |    4 +++-
 fs/unionfs/sioq.c   |    3 ++-
 fs/unionfs/sioq.h   |    1 +
 include/linux/fs.h  |    2 +-
 8 files changed, 24 insertions(+), 10 deletions(-)

--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -460,6 +460,7 @@ static int ecryptfs_symlink(struct inode
 {
 	int rc;
 	struct dentry *lower_dentry;
+	struct vfsmount *lower_mnt;
 	struct dentry *lower_dir_dentry;
 	umode_t mode;
 	char *encoded_symname;
@@ -468,6 +469,7 @@ static int ecryptfs_symlink(struct inode
 
 	lower_dentry = ecryptfs_dentry_to_lower(dentry);
 	dget(lower_dentry);
+	lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry);
 	lower_dir_dentry = lock_parent(lower_dentry);
 	mode = S_IALLUGO;
 	encoded_symlen = ecryptfs_encode_filename(crypt_stat, symname,
@@ -477,7 +479,7 @@ static int ecryptfs_symlink(struct inode
 		rc = encoded_symlen;
 		goto out_lock;
 	}
-	rc = vfs_symlink(lower_dir_dentry->d_inode, lower_dentry,
+	rc = vfs_symlink(lower_dir_dentry->d_inode, lower_dentry, lower_mnt,
 			 encoded_symname, mode);
 	kfree(encoded_symname);
 	if (rc || !lower_dentry->d_inode)
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2391,7 +2391,8 @@ asmlinkage long sys_unlink(const char __
 	return do_unlinkat(AT_FDCWD, pathname);
 }
 
-int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname, int mode)
+int vfs_symlink(struct inode *dir, struct dentry *dentry, struct vfsmount *mnt,
+		const char *oldname, int mode)
 {
 	int error = may_create(dir, dentry, NULL);
 
@@ -2440,7 +2441,8 @@ asmlinkage long sys_symlinkat(const char
 	error = mnt_want_write(nd.path.mnt);
 	if (error)
 		goto out_dput;
-	error = vfs_symlink(nd.path.dentry->d_inode, dentry, from, S_IALLUGO);
+	error = vfs_symlink(nd.path.dentry->d_inode, dentry, nd.path.mnt, from,
+			    S_IALLUGO);
 	mnt_drop_write(nd.path.mnt);
 out_dput:
 	dput(dentry);
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1495,6 +1495,7 @@ nfsd_symlink(struct svc_rqst *rqstp, str
 				struct iattr *iap)
 {
 	struct dentry	*dentry, *dnew;
+	struct svc_export *exp;
 	__be32		err, cerr;
 	int		host_err;
 	umode_t		mode;
@@ -1521,6 +1522,7 @@ nfsd_symlink(struct svc_rqst *rqstp, str
 	if (iap && (iap->ia_valid & ATTR_MODE))
 		mode = iap->ia_mode & S_IALLUGO;
 
+	exp = fhp->fh_export;
 	if (unlikely(path[plen] != 0)) {
 		char *path_alloced = kmalloc(plen+1, GFP_KERNEL);
 		if (path_alloced == NULL)
@@ -1528,20 +1530,23 @@ nfsd_symlink(struct svc_rqst *rqstp, str
 		else {
 			strncpy(path_alloced, path, plen);
 			path_alloced[plen] = 0;
-			host_err = vfs_symlink(dentry->d_inode, dnew, path_alloced, mode);
+			host_err = vfs_symlink(dentry->d_inode, dnew,
+					       exp->ex_path.mnt, path_alloced,
+					       mode);
 			kfree(path_alloced);
 		}
 	} else
-		host_err = vfs_symlink(dentry->d_inode, dnew, path, mode);
+		host_err = vfs_symlink(dentry->d_inode, dnew, exp->ex_path.mnt,
+				       path, mode);
 
 	if (!host_err) {
-		if (EX_ISSYNC(fhp->fh_export))
+		if (EX_ISSYNC(exp))
 			host_err = nfsd_sync_dir(dentry);
 	}
 	err = nfserrno(host_err);
 	fh_unlock(fhp);
 
-	cerr = fh_compose(resfhp, fhp->fh_export, dnew, fhp);
+	cerr = fh_compose(resfhp, exp, dnew, fhp);
 	dput(dnew);
 	if (err==0) err = cerr;
 out:
--- a/fs/unionfs/copyup.c
+++ b/fs/unionfs/copyup.c
@@ -184,6 +184,7 @@ static int __copyup_ndentry(struct dentr
 	} else if (S_ISLNK(old_mode)) {
 		args.symlink.parent = new_lower_parent_dentry->d_inode;
 		args.symlink.dentry = new_lower_dentry;
+		args.symlink.mnt = new_lower_mnt;
 		args.symlink.symbuf = symbuf;
 		args.symlink.mode = old_mode;
 
--- a/fs/unionfs/inode.c
+++ b/fs/unionfs/inode.c
@@ -452,6 +452,7 @@ static int unionfs_symlink(struct inode 
 	 */
 	for (bindex = bstart; bindex >= 0; bindex--) {
 		lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+		lower_mnt = unionfs_lower_mnt_idx(dentry, bindex);
 		if (!lower_dentry) {
 			/*
 			 * if lower_dentry is NULL, create the entire
@@ -482,7 +483,8 @@ static int unionfs_symlink(struct inode 
 		if (!err) {
 			mode = S_IALLUGO;
 			err = vfs_symlink(lower_dir_dentry->d_inode,
-					  lower_dentry, symname, mode);
+					  lower_dentry, lower_mnt, symname,
+					  mode);
 		}
 		unlock_dir(lower_dir_dentry);
 
--- a/fs/unionfs/sioq.c
+++ b/fs/unionfs/sioq.c
@@ -87,7 +87,8 @@ void __unionfs_symlink(struct work_struc
 	struct sioq_args *args = container_of(work, struct sioq_args, work);
 	struct symlink_args *s = &args->symlink;
 
-	args->err = vfs_symlink(s->parent, s->dentry, s->symbuf, s->mode);
+	args->err = vfs_symlink(s->parent, s->dentry, s->mnt, s->symbuf,
+				s->mode);
 	complete(&args->comp);
 }
 
--- a/fs/unionfs/sioq.h
+++ b/fs/unionfs/sioq.h
@@ -50,6 +50,7 @@ struct mknod_args {
 struct symlink_args {
 	struct inode *parent;
 	struct dentry *dentry;
+	struct vfsmount *mnt;
 	char *symbuf;
 	umode_t mode;
 };
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1072,7 +1072,7 @@ extern int vfs_permission(struct nameida
 extern int vfs_create(struct inode *, struct dentry *, int, struct nameidata *);
 extern int vfs_mkdir(struct inode *, struct dentry *, struct vfsmount *, int);
 extern int vfs_mknod(struct inode *, struct dentry *, struct vfsmount *, int, dev_t);
-extern int vfs_symlink(struct inode *, struct dentry *, const char *, int);
+extern int vfs_symlink(struct inode *, struct dentry *, struct vfsmount *, const char *, int);
 extern int vfs_link(struct dentry *, struct inode *, struct dentry *);
 extern int vfs_rmdir(struct inode *, struct dentry *);
 extern int vfs_unlink(struct inode *, struct dentry *);

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 10/47] Pass struct vfsmount to the inode_symlink LSM hook
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (8 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 09/47] Add a struct vfsmount parameter to vfs_symlink() John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 11/47] Pass struct vfsmount to the inode_readlink " John, Johansen
                   ` (36 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Tony Jones,
	Andreas Gruenbacher, John Johansen

[-- Attachment #1: security-symlink.diff --]
[-- Type: text/plain, Size: 3847 bytes --]

This is needed for computing pathnames in the AppArmor LSM.

Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/namei.c               |    2 +-
 include/linux/security.h |    8 +++++---
 security/dummy.c         |    2 +-
 security/security.c      |    4 ++--
 security/selinux/hooks.c |    3 ++-
 5 files changed, 11 insertions(+), 8 deletions(-)

--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2402,7 +2402,7 @@ int vfs_symlink(struct inode *dir, struc
 	if (!dir->i_op || !dir->i_op->symlink)
 		return -EPERM;
 
-	error = security_inode_symlink(dir, dentry, oldname);
+	error = security_inode_symlink(dir, dentry, mnt, oldname);
 	if (error)
 		return error;
 
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -332,6 +332,7 @@ struct request_sock;
  *	Check the permission to create a symbolic link to a file.
  *	@dir contains the inode structure of parent directory of the symbolic link.
  *	@dentry contains the dentry structure of the symbolic link.
+ *	@mnt is the vfsmount corresponding to @dentry (may be NULL).
  *	@old_name contains the pathname of file.
  *	Return 0 if permission is granted.
  * @inode_mkdir:
@@ -1281,8 +1282,8 @@ struct security_operations {
 	int (*inode_link) (struct dentry *old_dentry,
 	                   struct inode *dir, struct dentry *new_dentry);
 	int (*inode_unlink) (struct inode *dir, struct dentry *dentry);
-	int (*inode_symlink) (struct inode *dir,
-	                      struct dentry *dentry, const char *old_name);
+	int (*inode_symlink) (struct inode *dir, struct dentry *dentry,
+			      struct vfsmount *mnt, const char *old_name);
 	int (*inode_mkdir) (struct inode *dir, struct dentry *dentry,
 			    struct vfsmount *mnt, int mode);
 	int (*inode_rmdir) (struct inode *dir, struct dentry *dentry);
@@ -1548,7 +1549,7 @@ int security_inode_link(struct dentry *o
 			 struct dentry *new_dentry);
 int security_inode_unlink(struct inode *dir, struct dentry *dentry);
 int security_inode_symlink(struct inode *dir, struct dentry *dentry,
-			    const char *old_name);
+			   struct vfsmount *mnt, const char *old_name);
 int security_inode_mkdir(struct inode *dir, struct dentry *dentry,
 			 struct vfsmount *mnt, int mode);
 int security_inode_rmdir(struct inode *dir, struct dentry *dentry);
@@ -1878,6 +1879,7 @@ static inline int security_inode_unlink 
 
 static inline int security_inode_symlink (struct inode *dir,
 					  struct dentry *dentry,
+					  struct vfsmount *mnt,
 					  const char *old_name)
 {
 	return 0;
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -307,7 +307,7 @@ static int dummy_inode_unlink (struct in
 }
 
 static int dummy_inode_symlink (struct inode *inode, struct dentry *dentry,
-				const char *name)
+				struct vfsmount *mnt, const char *name)
 {
 	return 0;
 }
--- a/security/security.c
+++ b/security/security.c
@@ -372,11 +372,11 @@ int security_inode_unlink(struct inode *
 }
 
 int security_inode_symlink(struct inode *dir, struct dentry *dentry,
-			    const char *old_name)
+			   struct vfsmount *mnt, const char *old_name)
 {
 	if (unlikely(IS_PRIVATE(dir)))
 		return 0;
-	return security_ops->inode_symlink(dir, dentry, old_name);
+	return security_ops->inode_symlink(dir, dentry, mnt, old_name);
 }
 
 int security_inode_mkdir(struct inode *dir, struct dentry *dentry,
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2437,7 +2437,8 @@ static int selinux_inode_unlink(struct i
 	return may_link(dir, dentry, MAY_UNLINK);
 }
 
-static int selinux_inode_symlink(struct inode *dir, struct dentry *dentry, const char *name)
+static int selinux_inode_symlink(struct inode *dir, struct dentry *dentry,
+				 struct vfsmount *mnt, const char *name)
 {
 	return may_create(dir, dentry, SECCLASS_LNK_FILE);
 }

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 11/47] Pass struct vfsmount to the inode_readlink LSM hook
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (9 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 10/47] Pass struct vfsmount to the inode_symlink LSM hook John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 12/47] Add struct vfsmount parameters to vfs_link() John, Johansen
                   ` (35 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Tony Jones,
	Andreas Gruenbacher, John Johansen

[-- Attachment #1: security-readlink.diff --]
[-- Type: text/plain, Size: 3967 bytes --]

This is needed for computing pathnames in the AppArmor LSM.

Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/stat.c                |    3 ++-
 include/linux/security.h |    8 +++++---
 security/dummy.c         |    2 +-
 security/security.c      |    4 ++--
 security/selinux/hooks.c |    2 +-
 5 files changed, 11 insertions(+), 8 deletions(-)

--- a/fs/stat.c
+++ b/fs/stat.c
@@ -306,7 +306,8 @@ asmlinkage long sys_readlinkat(int dfd, 
 
 		error = -EINVAL;
 		if (inode->i_op && inode->i_op->readlink) {
-			error = security_inode_readlink(nd.path.dentry);
+			error = security_inode_readlink(nd.path.dentry,
+							nd.path.mnt);
 			if (!error) {
 				touch_atime(nd.path.mnt, nd.path.dentry);
 				error = inode->i_op->readlink(nd.path.dentry,
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -369,6 +369,7 @@ struct request_sock;
  * @inode_readlink:
  *	Check the permission to read the symbolic link.
  *	@dentry contains the dentry structure for the file link.
+ *	@mnt is the vfsmount corresponding to @dentry (may be NULL).
  *	Return 0 if permission is granted.
  * @inode_follow_link:
  *	Check permission to follow a symbolic link when looking up a pathname.
@@ -1291,7 +1292,7 @@ struct security_operations {
 			    struct vfsmount *mnt, int mode, dev_t dev);
 	int (*inode_rename) (struct inode *old_dir, struct dentry *old_dentry,
 	                     struct inode *new_dir, struct dentry *new_dentry);
-	int (*inode_readlink) (struct dentry *dentry);
+	int (*inode_readlink) (struct dentry *dentry, struct vfsmount *mnt);
 	int (*inode_follow_link) (struct dentry *dentry, struct nameidata *nd);
 	int (*inode_permission) (struct inode *inode, int mask, struct nameidata *nd);
 	int (*inode_setattr) (struct dentry *dentry, struct vfsmount *mnt,
@@ -1557,7 +1558,7 @@ int security_inode_mknod(struct inode *d
 			 struct vfsmount *mnt, int mode, dev_t dev);
 int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
 			   struct inode *new_dir, struct dentry *new_dentry);
-int security_inode_readlink(struct dentry *dentry);
+int security_inode_readlink(struct dentry *dentry, struct vfsmount *mnt);
 int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd);
 int security_inode_permission(struct inode *inode, int mask, struct nameidata *nd);
 int security_inode_setattr(struct dentry *dentry, struct vfsmount *mnt,
@@ -1915,7 +1916,8 @@ static inline int security_inode_rename 
 	return 0;
 }
 
-static inline int security_inode_readlink (struct dentry *dentry)
+static inline int security_inode_readlink(struct dentry *dentry,
+					   struct vfsmount *mnt)
 {
 	return 0;
 }
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -337,7 +337,7 @@ static int dummy_inode_rename (struct in
 	return 0;
 }
 
-static int dummy_inode_readlink (struct dentry *dentry)
+static int dummy_inode_readlink (struct dentry *dentry, struct vfsmount *mnt)
 {
 	return 0;
 }
--- a/security/security.c
+++ b/security/security.c
@@ -412,11 +412,11 @@ int security_inode_rename(struct inode *
 					   new_dir, new_dentry);
 }
 
-int security_inode_readlink(struct dentry *dentry)
+int security_inode_readlink(struct dentry *dentry, struct vfsmount *mnt)
 {
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 		return 0;
-	return security_ops->inode_readlink(dentry);
+	return security_ops->inode_readlink(dentry, mnt);
 }
 
 int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd)
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2472,7 +2472,7 @@ static int selinux_inode_rename(struct i
 	return may_rename(old_inode, old_dentry, new_inode, new_dentry);
 }
 
-static int selinux_inode_readlink(struct dentry *dentry)
+static int selinux_inode_readlink(struct dentry *dentry, struct vfsmount *mnt)
 {
 	return dentry_has_perm(current, NULL, dentry, FILE__READ);
 }

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 12/47] Add struct vfsmount parameters to vfs_link()
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (10 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 11/47] Pass struct vfsmount to the inode_readlink " John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 13/47] Pass the struct vfsmounts to the inode_link LSM hook John, Johansen
                   ` (34 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Tony Jones,
	Andreas Gruenbacher, John Johansen

[-- Attachment #1: vfs-link.diff --]
[-- Type: text/plain, Size: 5145 bytes --]

The vfsmount will be passed down to the LSM hook so that LSMs can compute
pathnames.

Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/ecryptfs/inode.c |    9 +++++++--
 fs/namei.c          |    5 +++--
 fs/nfsd/vfs.c       |    3 ++-
 fs/unionfs/inode.c  |   14 +++++++++++---
 include/linux/fs.h  |    2 +-
 5 files changed, 24 insertions(+), 9 deletions(-)

--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -401,19 +401,24 @@ static int ecryptfs_link(struct dentry *
 			 struct dentry *new_dentry)
 {
 	struct dentry *lower_old_dentry;
+	struct vfsmount *lower_old_mnt;
 	struct dentry *lower_new_dentry;
+	struct vfsmount *lower_new_mnt;
 	struct dentry *lower_dir_dentry;
 	u64 file_size_save;
 	int rc;
 
 	file_size_save = i_size_read(old_dentry->d_inode);
 	lower_old_dentry = ecryptfs_dentry_to_lower(old_dentry);
+	lower_old_mnt = ecryptfs_dentry_to_lower_mnt(old_dentry);
 	lower_new_dentry = ecryptfs_dentry_to_lower(new_dentry);
+	lower_new_mnt = ecryptfs_dentry_to_lower_mnt(new_dentry);
 	dget(lower_old_dentry);
 	dget(lower_new_dentry);
 	lower_dir_dentry = lock_parent(lower_new_dentry);
-	rc = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode,
-		      lower_new_dentry);
+	rc = vfs_link(lower_old_dentry, lower_old_mnt,
+		      lower_dir_dentry->d_inode, lower_new_dentry,
+		      lower_new_mnt);
 	if (rc || !lower_new_dentry->d_inode)
 		goto out_lock;
 	rc = ecryptfs_interpose(lower_new_dentry, new_dentry, dir->i_sb, 0);
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2461,7 +2461,7 @@ asmlinkage long sys_symlink(const char _
 	return sys_symlinkat(oldname, AT_FDCWD, newname);
 }
 
-int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
+int vfs_link(struct dentry *old_dentry, struct vfsmount *old_mnt, struct inode *dir, struct dentry *new_dentry, struct vfsmount *new_mnt)
 {
 	struct inode *inode = old_dentry->d_inode;
 	int error;
@@ -2542,7 +2542,8 @@ asmlinkage long sys_linkat(int olddfd, c
 	error = mnt_want_write(nd.path.mnt);
 	if (error)
 		goto out_dput;
-	error = vfs_link(old_nd.path.dentry, nd.path.dentry->d_inode, new_dentry);
+	error = vfs_link(old_nd.path.dentry, old_nd.path.mnt,
+			 nd.path.dentry->d_inode, new_dentry, nd.path.mnt);
 	mnt_drop_write(nd.path.mnt);
 out_dput:
 	dput(new_dentry);
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1596,7 +1596,8 @@ nfsd_link(struct svc_rqst *rqstp, struct
 	dold = tfhp->fh_dentry;
 	dest = dold->d_inode;
 
-	host_err = vfs_link(dold, dirp, dnew);
+	host_err = vfs_link(dold, tfhp->fh_export->ex_path.mnt, dirp,
+			    dnew, ffhp->fh_export->ex_path.mnt);
 	if (!host_err) {
 		if (EX_ISSYNC(ffhp->fh_export)) {
 			err = nfserrno(nfsd_sync_dir(ddir));
--- a/fs/unionfs/inode.c
+++ b/fs/unionfs/inode.c
@@ -222,6 +222,7 @@ static int unionfs_link(struct dentry *o
 {
 	int err = 0;
 	struct dentry *lower_old_dentry = NULL;
+	struct vfsmount *lower_old_mnt = NULL;
 	struct dentry *lower_new_dentry = NULL;
 	struct vfsmount *lower_new_mnt = NULL;
 	struct dentry *lower_dir_dentry = NULL;
@@ -293,14 +294,17 @@ static int unionfs_link(struct dentry *o
 			goto out;
 	}
 	lower_new_dentry = unionfs_lower_dentry(new_dentry);
+	lower_new_mnt = unionfs_lower_mnt(new_dentry);
 	lower_old_dentry = unionfs_lower_dentry(old_dentry);
+	lower_old_mnt = unionfs_lower_mnt(old_dentry);
 
 	BUG_ON(dbstart(old_dentry) != dbstart(new_dentry));
 	lower_dir_dentry = lock_parent(lower_new_dentry);
 	err = is_robranch(old_dentry);
 	if (!err)
-		err = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode,
-			       lower_new_dentry);
+		err = vfs_link(lower_old_dentry, lower_old_mnt,
+			       lower_dir_dentry->d_inode,
+			       lower_new_dentry, lower_new_mnt);
 	unlock_dir(lower_dir_dentry);
 
 docopyup:
@@ -321,12 +325,16 @@ docopyup:
 						       bindex, &lower_new_mnt);
 				lower_old_dentry =
 					unionfs_lower_dentry(old_dentry);
+				lower_old_mnt =
+					unionfs_lower_mnt(old_dentry);
 				lower_dir_dentry =
 					lock_parent(lower_new_dentry);
 				/* do vfs_link */
 				err = vfs_link(lower_old_dentry,
+					       lower_old_mnt,
 					       lower_dir_dentry->d_inode,
-					       lower_new_dentry);
+					       lower_new_dentry,
+					       lower_new_mnt);
 				unlock_dir(lower_dir_dentry);
 				goto check_link;
 			}
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1073,7 +1073,7 @@ extern int vfs_create(struct inode *, st
 extern int vfs_mkdir(struct inode *, struct dentry *, struct vfsmount *, int);
 extern int vfs_mknod(struct inode *, struct dentry *, struct vfsmount *, int, dev_t);
 extern int vfs_symlink(struct inode *, struct dentry *, struct vfsmount *, const char *, int);
-extern int vfs_link(struct dentry *, struct inode *, struct dentry *);
+extern int vfs_link(struct dentry *, struct vfsmount *, struct inode *, struct dentry *, struct vfsmount *);
 extern int vfs_rmdir(struct inode *, struct dentry *);
 extern int vfs_unlink(struct inode *, struct dentry *);
 extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 13/47] Pass the struct vfsmounts to the inode_link LSM hook
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (11 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 12/47] Add struct vfsmount parameters to vfs_link() John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 14/47] Add a struct vfsmount parameter to vfs_rmdir() John, Johansen
                   ` (33 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Tony Jones,
	Andreas Gruenbacher, John Johansen

[-- Attachment #1: security-link.diff --]
[-- Type: text/plain, Size: 5085 bytes --]

This is needed for computing pathnames in the AppArmor LSM.

Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/namei.c               |    3 ++-
 include/linux/security.h |   16 +++++++++++-----
 security/dummy.c         |    6 ++++--
 security/security.c      |    8 +++++---
 security/selinux/hooks.c |    9 +++++++--
 5 files changed, 29 insertions(+), 13 deletions(-)

--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2486,7 +2486,8 @@ int vfs_link(struct dentry *old_dentry, 
 	if (S_ISDIR(old_dentry->d_inode->i_mode))
 		return -EPERM;
 
-	error = security_inode_link(old_dentry, dir, new_dentry);
+	error = security_inode_link(old_dentry, old_mnt, dir, new_dentry,
+				    new_mnt);
 	if (error)
 		return error;
 
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -320,8 +320,10 @@ struct request_sock;
  * @inode_link:
  *	Check permission before creating a new hard link to a file.
  *	@old_dentry contains the dentry structure for an existing link to the file.
+ *	@old_mnt is the vfsmount corresponding to @old_dentry (may be NULL).
  *	@dir contains the inode structure of the parent directory of the new link.
  *	@new_dentry contains the dentry structure for the new link.
+ *	@new_mnt is the vfsmount corresponding to @new_dentry (may be NULL).
  *	Return 0 if permission is granted.
  * @inode_unlink:
  *	Check the permission to remove a hard link to a file. 
@@ -1280,8 +1282,9 @@ struct security_operations {
 				    char **name, void **value, size_t *len);
 	int (*inode_create) (struct inode *dir, struct dentry *dentry,
 			     struct vfsmount *mnt, int mode);
-	int (*inode_link) (struct dentry *old_dentry,
-	                   struct inode *dir, struct dentry *new_dentry);
+	int (*inode_link) (struct dentry *old_dentry, struct vfsmount *old_mnt,
+	                   struct inode *dir, struct dentry *new_dentry,
+			   struct vfsmount *new_mnt);
 	int (*inode_unlink) (struct inode *dir, struct dentry *dentry);
 	int (*inode_symlink) (struct inode *dir, struct dentry *dentry,
 			      struct vfsmount *mnt, const char *old_name);
@@ -1546,8 +1549,9 @@ int security_inode_init_security(struct 
 				  char **name, void **value, size_t *len);
 int security_inode_create(struct inode *dir, struct dentry *dentry,
 			  struct vfsmount *mnt, int mode);
-int security_inode_link(struct dentry *old_dentry, struct inode *dir,
-			 struct dentry *new_dentry);
+int security_inode_link(struct dentry *old_dentry, struct vfsmount *old_mnt,
+			struct inode *dir, struct dentry *new_dentry,
+			struct vfsmount *new_mnt);
 int security_inode_unlink(struct inode *dir, struct dentry *dentry);
 int security_inode_symlink(struct inode *dir, struct dentry *dentry,
 			   struct vfsmount *mnt, const char *old_name);
@@ -1866,8 +1870,10 @@ static inline int security_inode_create 
 }
 
 static inline int security_inode_link (struct dentry *old_dentry,
+				       struct vfsmount *old_mnt,
 				       struct inode *dir,
-				       struct dentry *new_dentry)
+				       struct dentry *new_dentry,
+				       struct vfsmount *new_mnt)
 {
 	return 0;
 }
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -295,8 +295,10 @@ static int dummy_inode_create (struct in
 	return 0;
 }
 
-static int dummy_inode_link (struct dentry *old_dentry, struct inode *inode,
-			     struct dentry *new_dentry)
+static int dummy_inode_link (struct dentry *old_dentry,
+			     struct vfsmount *old_mnt, struct inode *inode,
+			     struct dentry *new_dentry,
+			     struct vfsmount *new_mnt)
 {
 	return 0;
 }
--- a/security/security.c
+++ b/security/security.c
@@ -356,12 +356,14 @@ int security_inode_create(struct inode *
 	return security_ops->inode_create(dir, dentry, mnt, mode);
 }
 
-int security_inode_link(struct dentry *old_dentry, struct inode *dir,
-			 struct dentry *new_dentry)
+int security_inode_link(struct dentry *old_dentry, struct vfsmount *old_mnt,
+			struct inode *dir, struct dentry *new_dentry,
+			struct vfsmount *new_mnt)
 {
 	if (unlikely(IS_PRIVATE(old_dentry->d_inode)))
 		return 0;
-	return security_ops->inode_link(old_dentry, dir, new_dentry);
+	return security_ops->inode_link(old_dentry, old_mnt, dir,
+					 new_dentry, new_mnt);
 }
 
 int security_inode_unlink(struct inode *dir, struct dentry *dentry)
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2417,11 +2417,16 @@ static int selinux_inode_create(struct i
 	return may_create(dir, dentry, SECCLASS_FILE);
 }
 
-static int selinux_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
+static int selinux_inode_link(struct dentry *old_dentry,
+			      struct vfsmount *old_mnt,
+			      struct inode *dir,
+			      struct dentry *new_dentry,
+			      struct vfsmount *new_mnt)
 {
 	int rc;
 
-	rc = secondary_ops->inode_link(old_dentry,dir,new_dentry);
+	rc = secondary_ops->inode_link(old_dentry, old_mnt, dir, new_dentry,
+				       new_mnt);
 	if (rc)
 		return rc;
 	return may_link(dir, old_dentry, MAY_LINK);

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 14/47] Add a struct vfsmount parameter to vfs_rmdir()
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (12 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 13/47] Pass the struct vfsmounts to the inode_link LSM hook John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 15/47] Pass struct vfsmount to the inode_rmdir LSM hook John, Johansen
                   ` (32 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Tony Jones,
	Andreas Gruenbacher, John Johansen

[-- Attachment #1: vfs-rmdir.diff --]
[-- Type: text/plain, Size: 5305 bytes --]

The vfsmount will be passed down to the LSM hook so that LSMs can compute
pathnames.

Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/ecryptfs/inode.c   |    4 +++-
 fs/namei.c            |    4 ++--
 fs/nfsd/nfs4recover.c |    2 +-
 fs/nfsd/vfs.c         |    8 +++++---
 fs/reiserfs/xattr.c   |    2 +-
 fs/unionfs/unlink.c   |    5 ++++-
 include/linux/fs.h    |    2 +-
 7 files changed, 17 insertions(+), 10 deletions(-)

--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -532,14 +532,16 @@ out:
 static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry)
 {
 	struct dentry *lower_dentry;
+	struct vfsmount *lower_mnt;
 	struct dentry *lower_dir_dentry;
 	int rc;
 
 	lower_dentry = ecryptfs_dentry_to_lower(dentry);
+	lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry);
 	dget(dentry);
 	lower_dir_dentry = lock_parent(lower_dentry);
 	dget(lower_dentry);
-	rc = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry);
+	rc = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry, lower_mnt);
 	dput(lower_dentry);
 	if (!rc)
 		d_delete(lower_dentry);
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2202,7 +2202,7 @@ void dentry_unhash(struct dentry *dentry
 	spin_unlock(&dcache_lock);
 }
 
-int vfs_rmdir(struct inode *dir, struct dentry *dentry)
+int vfs_rmdir(struct inode *dir, struct dentry *dentry,struct vfsmount *mnt)
 {
 	int error = may_delete(dir, dentry, 1);
 
@@ -2269,7 +2269,7 @@ static long do_rmdir(int dfd, const char
 	error = mnt_want_write(nd.path.mnt);
 	if (error)
 		goto exit3;
-	error = vfs_rmdir(nd.path.dentry->d_inode, dentry);
+	error = vfs_rmdir(nd.path.dentry->d_inode, dentry, nd.path.mnt);
 	mnt_drop_write(nd.path.mnt);
 exit3:
 	dput(dentry);
--- a/fs/nfsd/nfs4recover.c
+++ b/fs/nfsd/nfs4recover.c
@@ -279,7 +279,7 @@ nfsd4_clear_clid_dir(struct dentry *dir,
 	 * a kernel from the future.... */
 	nfsd4_list_rec_dir(dentry, nfsd4_remove_clid_file);
 	mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT);
-	status = vfs_rmdir(dir->d_inode, dentry);
+	status = vfs_rmdir(dir->d_inode, dentry, rec_dir.path.mnt);
 	mutex_unlock(&dir->d_inode->i_mutex);
 	return status;
 }
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1734,6 +1734,7 @@ nfsd_unlink(struct svc_rqst *rqstp, stru
 				char *fname, int flen)
 {
 	struct dentry	*dentry, *rdentry;
+	struct svc_export *exp;
 	struct inode	*dirp;
 	__be32		err;
 	int		host_err;
@@ -1748,6 +1749,7 @@ nfsd_unlink(struct svc_rqst *rqstp, stru
 	fh_lock_nested(fhp, I_MUTEX_PARENT);
 	dentry = fhp->fh_dentry;
 	dirp = dentry->d_inode;
+	exp = fhp->fh_export;
 
 	rdentry = lookup_one_len(fname, dentry, flen);
 	host_err = PTR_ERR(rdentry);
@@ -1765,21 +1767,21 @@ nfsd_unlink(struct svc_rqst *rqstp, stru
 
 	if (type != S_IFDIR) { /* It's UNLINK */
 #ifdef MSNFS
-		if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) &&
+		if ((exp->ex_flags & NFSEXP_MSNFS) &&
 			(atomic_read(&rdentry->d_count) > 1)) {
 			host_err = -EPERM;
 		} else
 #endif
 		host_err = vfs_unlink(dirp, rdentry);
 	} else { /* It's RMDIR */
-		host_err = vfs_rmdir(dirp, rdentry);
+		host_err = vfs_rmdir(dirp, rdentry, exp->ex_path.mnt);
 	}
 
 	dput(rdentry);
 
 	if (host_err)
 		goto out_nfserr;
-	if (EX_ISSYNC(fhp->fh_export))
+	if (EX_ISSYNC(exp))
 		host_err = nfsd_sync_dir(dentry);
 
 out_nfserr:
--- a/fs/reiserfs/xattr.c
+++ b/fs/reiserfs/xattr.c
@@ -747,7 +747,7 @@ int reiserfs_delete_xattrs(struct inode 
 	if (dir->d_inode->i_nlink <= 2) {
 		root = get_xa_root(inode->i_sb, XATTR_REPLACE);
 		reiserfs_write_lock_xattrs(inode->i_sb);
-		err = vfs_rmdir(root->d_inode, dir);
+		err = vfs_rmdir(root->d_inode, dir, NULL);
 		reiserfs_write_unlock_xattrs(inode->i_sb);
 		dput(root);
 	} else {
--- a/fs/unionfs/unlink.c
+++ b/fs/unionfs/unlink.c
@@ -125,6 +125,7 @@ static int unionfs_rmdir_first(struct in
 {
 	int err;
 	struct dentry *lower_dentry;
+	struct vfsmount *lower_mnt;
 	struct dentry *lower_dir_dentry = NULL;
 
 	/* Here we need to remove whiteout entries. */
@@ -133,6 +134,7 @@ static int unionfs_rmdir_first(struct in
 		goto out;
 
 	lower_dentry = unionfs_lower_dentry(dentry);
+	lower_mnt = unionfs_lower_mnt(dentry);
 
 	lower_dir_dentry = lock_parent(lower_dentry);
 
@@ -140,7 +142,8 @@ static int unionfs_rmdir_first(struct in
 	dget(lower_dentry);
 	err = is_robranch(dentry);
 	if (!err)
-		err = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry);
+		err = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry,
+				lower_mnt);
 	dput(lower_dentry);
 
 	fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode);
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1074,7 +1074,7 @@ extern int vfs_mkdir(struct inode *, str
 extern int vfs_mknod(struct inode *, struct dentry *, struct vfsmount *, int, dev_t);
 extern int vfs_symlink(struct inode *, struct dentry *, struct vfsmount *, const char *, int);
 extern int vfs_link(struct dentry *, struct vfsmount *, struct inode *, struct dentry *, struct vfsmount *);
-extern int vfs_rmdir(struct inode *, struct dentry *);
+extern int vfs_rmdir(struct inode *, struct dentry *, struct vfsmount *);
 extern int vfs_unlink(struct inode *, struct dentry *);
 extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
 

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 15/47] Pass struct vfsmount to the inode_rmdir LSM hook
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (13 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 14/47] Add a struct vfsmount parameter to vfs_rmdir() John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 16/47] Call lsm hook before unhashing dentry in vfs_rmdir() John, Johansen
                   ` (31 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Tony Jones,
	Andreas Gruenbacher, John Johansen

[-- Attachment #1: security-rmdir.diff --]
[-- Type: text/plain, Size: 3967 bytes --]

This is needed for computing pathnames in the AppArmor LSM.

Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/namei.c               |    2 +-
 include/linux/security.h |   10 +++++++---
 security/dummy.c         |    3 ++-
 security/security.c      |    5 +++--
 security/selinux/hooks.c |    3 ++-
 5 files changed, 15 insertions(+), 8 deletions(-)

--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2219,7 +2219,7 @@ int vfs_rmdir(struct inode *dir, struct 
 	if (d_mountpoint(dentry))
 		error = -EBUSY;
 	else {
-		error = security_inode_rmdir(dir, dentry);
+		error = security_inode_rmdir(dir, dentry, mnt);
 		if (!error) {
 			error = dir->i_op->rmdir(dir, dentry);
 			if (!error)
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -349,6 +349,7 @@ struct request_sock;
  *	Check the permission to remove a directory.
  *	@dir contains the inode structure of parent of the directory to be removed.
  *	@dentry contains the dentry structure of directory to be removed.
+ *	@mnt is the vfsmount corresponding to @dentry (may be NULL).
  *	Return 0 if permission is granted.
  * @inode_mknod:
  *	Check permissions when creating a special file (or a socket or a fifo
@@ -1290,7 +1291,8 @@ struct security_operations {
 			      struct vfsmount *mnt, const char *old_name);
 	int (*inode_mkdir) (struct inode *dir, struct dentry *dentry,
 			    struct vfsmount *mnt, int mode);
-	int (*inode_rmdir) (struct inode *dir, struct dentry *dentry);
+	int (*inode_rmdir) (struct inode *dir, struct dentry *dentry,
+			    struct vfsmount *mnt);
 	int (*inode_mknod) (struct inode *dir, struct dentry *dentry,
 			    struct vfsmount *mnt, int mode, dev_t dev);
 	int (*inode_rename) (struct inode *old_dir, struct dentry *old_dentry,
@@ -1557,7 +1559,8 @@ int security_inode_symlink(struct inode 
 			   struct vfsmount *mnt, const char *old_name);
 int security_inode_mkdir(struct inode *dir, struct dentry *dentry,
 			 struct vfsmount *mnt, int mode);
-int security_inode_rmdir(struct inode *dir, struct dentry *dentry);
+int security_inode_rmdir(struct inode *dir, struct dentry *dentry,
+			 struct vfsmount *mnt);
 int security_inode_mknod(struct inode *dir, struct dentry *dentry,
 			 struct vfsmount *mnt, int mode, dev_t dev);
 int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
@@ -1901,7 +1904,8 @@ static inline int security_inode_mkdir (
 }
 
 static inline int security_inode_rmdir (struct inode *dir,
-					struct dentry *dentry)
+					struct dentry *dentry,
+					struct vfsmount *mnt)
 {
 	return 0;
 }
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -320,7 +320,8 @@ static int dummy_inode_mkdir (struct ino
 	return 0;
 }
 
-static int dummy_inode_rmdir (struct inode *inode, struct dentry *dentry)
+static int dummy_inode_rmdir (struct inode *inode, struct dentry *dentry,
+			      struct vfsmount *mnt)
 {
 	return 0;
 }
--- a/security/security.c
+++ b/security/security.c
@@ -389,11 +389,12 @@ int security_inode_mkdir(struct inode *d
 	return security_ops->inode_mkdir(dir, dentry, mnt, mode);
 }
 
-int security_inode_rmdir(struct inode *dir, struct dentry *dentry)
+int security_inode_rmdir(struct inode *dir, struct dentry *dentry,
+			 struct vfsmount *mnt)
 {
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 		return 0;
-	return security_ops->inode_rmdir(dir, dentry);
+	return security_ops->inode_rmdir(dir, dentry, mnt);
 }
 
 int security_inode_mknod(struct inode *dir, struct dentry *dentry,
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2454,7 +2454,8 @@ static int selinux_inode_mkdir(struct in
 	return may_create(dir, dentry, SECCLASS_DIR);
 }
 
-static int selinux_inode_rmdir(struct inode *dir, struct dentry *dentry)
+static int selinux_inode_rmdir(struct inode *dir, struct dentry *dentry,
+			       struct vfsmount *mnt)
 {
 	return may_link(dir, dentry, MAY_RMDIR);
 }

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 16/47] Call lsm hook before unhashing dentry in vfs_rmdir()
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (14 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 15/47] Pass struct vfsmount to the inode_rmdir LSM hook John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 17/47] Add a struct vfsmount parameter to vfs_unlink() John, Johansen
                   ` (30 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, John Johansen,
	Andreas Gruenbacher

[-- Attachment #1: fix-vfs_rmdir.diff --]
[-- Type: text/plain, Size: 1173 bytes --]

If we unhash the dentry before calling the security_inode_rmdir hook,
we cannot compute the file's pathname in the hook anymore. AppArmor
needs to know the filename in order to decide whether a file may be
deleted, though.

Signed-off-by: John Johansen <jjohansen@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>

---
 fs/namei.c |   13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2212,6 +2212,10 @@ int vfs_rmdir(struct inode *dir, struct 
 	if (!dir->i_op || !dir->i_op->rmdir)
 		return -EPERM;
 
+	error = security_inode_rmdir(dir, dentry, mnt);
+	if (error)
+		return error;
+
 	DQUOT_INIT(dir);
 
 	mutex_lock(&dentry->d_inode->i_mutex);
@@ -2219,12 +2223,9 @@ int vfs_rmdir(struct inode *dir, struct 
 	if (d_mountpoint(dentry))
 		error = -EBUSY;
 	else {
-		error = security_inode_rmdir(dir, dentry, mnt);
-		if (!error) {
-			error = dir->i_op->rmdir(dir, dentry);
-			if (!error)
-				dentry->d_inode->i_flags |= S_DEAD;
-		}
+		error = dir->i_op->rmdir(dir, dentry);
+		if (!error)
+			dentry->d_inode->i_flags |= S_DEAD;
 	}
 	mutex_unlock(&dentry->d_inode->i_mutex);
 	if (!error) {

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 17/47] Add a struct vfsmount parameter to vfs_unlink()
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (15 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 16/47] Call lsm hook before unhashing dentry in vfs_rmdir() John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 18/47] Pass struct vfsmount to the inode_unlink LSM hook John, Johansen
                   ` (29 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Tony Jones,
	Andreas Gruenbacher, John Johansen

[-- Attachment #1: vfs-unlink.diff --]
[-- Type: text/plain, Size: 13519 bytes --]

The vfsmount will be passed down to the LSM hook so that LSMs can compute
pathnames.

Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/ecryptfs/inode.c     |    3 ++-
 fs/namei.c              |    5 +++--
 fs/nfsd/nfs4recover.c   |    2 +-
 fs/nfsd/vfs.c           |    2 +-
 fs/unionfs/commonfops.c |    4 +++-
 fs/unionfs/copyup.c     |    3 ++-
 fs/unionfs/dirhelper.c  |    5 ++++-
 fs/unionfs/inode.c      |   22 ++++++++++++++++------
 fs/unionfs/rename.c     |    8 ++++++--
 fs/unionfs/sioq.c       |    2 +-
 fs/unionfs/sioq.h       |    1 +
 fs/unionfs/unlink.c     |    5 ++++-
 include/linux/fs.h      |    2 +-
 ipc/mqueue.c            |    2 +-
 14 files changed, 46 insertions(+), 20 deletions(-)

--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -443,10 +443,11 @@ static int ecryptfs_unlink(struct inode 
 {
 	int rc = 0;
 	struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
+	struct vfsmount *lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry);
 	struct inode *lower_dir_inode = ecryptfs_inode_to_lower(dir);
 
 	lock_parent(lower_dentry);
-	rc = vfs_unlink(lower_dir_inode, lower_dentry);
+	rc = vfs_unlink(lower_dir_inode, lower_dentry, lower_mnt);
 	if (rc) {
 		printk(KERN_ERR "Error in vfs_unlink; rc = [%d]\n", rc);
 		goto out_unlock;
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2288,7 +2288,7 @@ asmlinkage long sys_rmdir(const char __u
 	return do_rmdir(AT_FDCWD, pathname);
 }
 
-int vfs_unlink(struct inode *dir, struct dentry *dentry)
+int vfs_unlink(struct inode *dir, struct dentry *dentry, struct vfsmount *mnt)
 {
 	int error = may_delete(dir, dentry, 0);
 
@@ -2356,7 +2356,8 @@ static long do_unlinkat(int dfd, const c
 		error = mnt_want_write(nd.path.mnt);
 		if (error)
 			goto exit2;
-		error = vfs_unlink(nd.path.dentry->d_inode, dentry);
+		error = vfs_unlink(nd.path.dentry->d_inode, dentry,
+				   nd.path.mnt);
 		mnt_drop_write(nd.path.mnt);
 	exit2:
 		dput(dentry);
--- a/fs/nfsd/nfs4recover.c
+++ b/fs/nfsd/nfs4recover.c
@@ -264,7 +264,7 @@ nfsd4_remove_clid_file(struct dentry *di
 		return -EINVAL;
 	}
 	mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT);
-	status = vfs_unlink(dir->d_inode, dentry);
+	status = vfs_unlink(dir->d_inode, dentry, rec_dir.path.mnt);
 	mutex_unlock(&dir->d_inode->i_mutex);
 	return status;
 }
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1772,7 +1772,7 @@ nfsd_unlink(struct svc_rqst *rqstp, stru
 			host_err = -EPERM;
 		} else
 #endif
-		host_err = vfs_unlink(dirp, rdentry);
+		host_err = vfs_unlink(dirp, rdentry, exp->ex_path.mnt);
 	} else { /* It's RMDIR */
 		host_err = vfs_rmdir(dirp, rdentry, exp->ex_path.mnt);
 	}
--- a/fs/unionfs/commonfops.c
+++ b/fs/unionfs/commonfops.c
@@ -34,6 +34,7 @@ static int copyup_deleted_file(struct fi
 	int err;
 	struct dentry *tmp_dentry = NULL;
 	struct dentry *lower_dentry;
+	struct vfsmount *lower_mnt;
 	struct dentry *lower_dir_dentry = NULL;
 
 	lower_dentry = unionfs_lower_dentry_idx(dentry, bstart);
@@ -82,13 +83,14 @@ retry:
 
 	/* bring it to the same state as an unlinked file */
 	lower_dentry = unionfs_lower_dentry_idx(dentry, dbstart(dentry));
+	lower_mnt = unionfs_lower_mnt_idx(dentry, dbstart(dentry));
 	if (!unionfs_lower_inode_idx(dentry->d_inode, bindex)) {
 		atomic_inc(&lower_dentry->d_inode->i_count);
 		unionfs_set_lower_inode_idx(dentry->d_inode, bindex,
 					    lower_dentry->d_inode);
 	}
 	lower_dir_dentry = lock_parent(lower_dentry);
-	err = vfs_unlink(lower_dir_dentry->d_inode, lower_dentry);
+	err = vfs_unlink(lower_dir_dentry->d_inode, lower_dentry, lower_mnt);
 	unlock_dir(lower_dir_dentry);
 
 out:
--- a/fs/unionfs/copyup.c
+++ b/fs/unionfs/copyup.c
@@ -491,7 +491,8 @@ out_unlink:
 	 * quota, or something else happened so let's unlink; we don't
 	 * really care about the return value of vfs_unlink
 	 */
-	vfs_unlink(new_lower_parent_dentry->d_inode, new_lower_dentry);
+	vfs_unlink(new_lower_parent_dentry->d_inode, new_lower_dentry,
+		   new_lower_mnt);
 
 	if (copyup_file) {
 		/* need to close the file */
--- a/fs/unionfs/dirhelper.c
+++ b/fs/unionfs/dirhelper.c
@@ -29,6 +29,7 @@ int do_delete_whiteouts(struct dentry *d
 	int err = 0;
 	struct dentry *lower_dir_dentry = NULL;
 	struct dentry *lower_dentry;
+	struct vfsmount *lower_mnt;
 	char *name = NULL, *p;
 	struct inode *lower_dir;
 	int i;
@@ -37,6 +38,7 @@ int do_delete_whiteouts(struct dentry *d
 
 	/* Find out lower parent dentry */
 	lower_dir_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+	lower_mnt = unionfs_lower_mnt_idx(dentry, bindex);
 	BUG_ON(!S_ISDIR(lower_dir_dentry->d_inode->i_mode));
 	lower_dir = lower_dir_dentry->d_inode;
 	BUG_ON(!S_ISDIR(lower_dir->i_mode));
@@ -70,7 +72,8 @@ int do_delete_whiteouts(struct dentry *d
 				break;
 			}
 			if (lower_dentry->d_inode)
-				err = vfs_unlink(lower_dir, lower_dentry);
+				err = vfs_unlink(lower_dir, lower_dentry,
+						 lower_mnt);
 			dput(lower_dentry);
 			if (err)
 				break;
--- a/fs/unionfs/inode.c
+++ b/fs/unionfs/inode.c
@@ -25,6 +25,7 @@ static int unionfs_create(struct inode *
 	struct dentry *lower_dentry = NULL;
 	struct vfsmount *lower_mnt = NULL;
 	struct dentry *wh_dentry = NULL;
+	struct vfsmount *wh_mnt = NULL;
 	struct dentry *lower_parent_dentry = NULL;
 	char *name = NULL;
 	int valid = 0;
@@ -75,6 +76,7 @@ static int unionfs_create(struct inode *
 
 		wh_dentry = lookup_one_len(name, lower_dentry->d_parent,
 					   dentry->d_name.len + UNIONFS_WHLEN);
+		wh_mnt = unionfs_lower_mnt(dentry);
 		if (IS_ERR(wh_dentry)) {
 			err = PTR_ERR(wh_dentry);
 			wh_dentry = NULL;
@@ -88,7 +90,8 @@ static int unionfs_create(struct inode *
 			struct dentry *lower_dir_dentry;
 
 			lower_dir_dentry = lock_parent(wh_dentry);
-			err = vfs_unlink(lower_dir_dentry->d_inode, wh_dentry);
+			err = vfs_unlink(lower_dir_dentry->d_inode, wh_dentry,
+					 wh_mnt);
 			unlock_dir(lower_dir_dentry);
 
 			/*
@@ -227,6 +230,7 @@ static int unionfs_link(struct dentry *o
 	struct vfsmount *lower_new_mnt = NULL;
 	struct dentry *lower_dir_dentry = NULL;
 	struct dentry *whiteout_dentry;
+	struct vfsmount *whiteout_mnt;
 	char *name = NULL;
 
 	unionfs_read_lock(old_dentry->d_sb);
@@ -257,6 +261,7 @@ static int unionfs_link(struct dentry *o
 	whiteout_dentry = lookup_one_len(name, lower_new_dentry->d_parent,
 					 new_dentry->d_name.len +
 					 UNIONFS_WHLEN);
+	whiteout_mnt = unionfs_lower_mnt(new_dentry);
 	if (IS_ERR(whiteout_dentry)) {
 		err = PTR_ERR(whiteout_dentry);
 		goto out;
@@ -271,7 +276,7 @@ static int unionfs_link(struct dentry *o
 		err = is_robranch_super(new_dentry->d_sb, dbstart(new_dentry));
 		if (!err)
 			err = vfs_unlink(lower_dir_dentry->d_inode,
-					 whiteout_dentry);
+					 whiteout_dentry, whiteout_mnt);
 
 		fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode);
 		dir->i_nlink = unionfs_get_nlinks(dir);
@@ -383,6 +388,7 @@ static int unionfs_symlink(struct inode 
 	struct dentry *lower_dentry = NULL;
 	struct vfsmount *lower_mnt = NULL;
 	struct dentry *whiteout_dentry = NULL;
+	struct vfsmount *whiteout_mnt = NULL;
 	struct dentry *lower_dir_dentry = NULL;
 	umode_t mode;
 	int bindex = 0, bstart;
@@ -428,12 +434,13 @@ static int unionfs_symlink(struct inode 
 		 * found a .wh.foo entry, unlink it and then call
 		 * vfs_symlink().
 		 */
+		whiteout_mnt = unionfs_lower_mnt(dentry);
 		lower_dir_dentry = lock_parent(whiteout_dentry);
 
 		err = is_robranch_super(dentry->d_sb, bstart);
 		if (!err)
 			err = vfs_unlink(lower_dir_dentry->d_inode,
-					 whiteout_dentry);
+					 whiteout_dentry, whiteout_mnt);
 		dput(whiteout_dentry);
 
 		fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode);
@@ -547,7 +554,7 @@ static int unionfs_mkdir(struct inode *p
 {
 	int err = 0;
 	struct dentry *lower_dentry = NULL, *whiteout_dentry = NULL;
-	struct vfsmount *lower_mnt = NULL;
+	struct vfsmount *lower_mnt = NULL, *whiteout_mnt = NULL;
 	struct dentry *lower_parent_dentry = NULL;
 	int bindex = 0, bstart;
 	char *name = NULL;
@@ -588,6 +595,7 @@ static int unionfs_mkdir(struct inode *p
 		dput(whiteout_dentry);
 		whiteout_dentry = NULL;
 	} else {
+		whiteout_mnt = unionfs_lower_mnt(dentry);
 		lower_parent_dentry = lock_parent(whiteout_dentry);
 
 		/* found a.wh.foo entry, remove it then do vfs_mkdir */
@@ -595,6 +603,7 @@ static int unionfs_mkdir(struct inode *p
 		if (!err) {
 			args.unlink.parent = lower_parent_dentry->d_inode;
 			args.unlink.dentry = whiteout_dentry;
+			args.unlink.mnt = whiteout_mnt;
 			run_sioq(__unionfs_unlink, &args);
 			err = args.err;
 		}
@@ -704,7 +713,7 @@ static int unionfs_mknod(struct inode *d
 {
 	int err = 0;
 	struct dentry *lower_dentry = NULL, *whiteout_dentry = NULL;
-	struct vfsmount *lower_mnt = NULL;
+	struct vfsmount *lower_mnt = NULL, *whiteout_mnt = NULL;
 	struct dentry *lower_parent_dentry = NULL;
 	int bindex = 0, bstart;
 	char *name = NULL;
@@ -745,13 +754,14 @@ static int unionfs_mknod(struct inode *d
 		whiteout_dentry = NULL;
 	} else {
 		/* found .wh.foo, unlink it */
+		whiteout_mnt = unionfs_lower_mnt(dentry);
 		lower_parent_dentry = lock_parent(whiteout_dentry);
 
 		/* found a.wh.foo entry, remove it then do vfs_mkdir */
 		err = is_robranch_super(dentry->d_sb, bstart);
 		if (!err)
 			err = vfs_unlink(lower_parent_dentry->d_inode,
-					 whiteout_dentry);
+					 whiteout_dentry, whiteout_mnt);
 		dput(whiteout_dentry);
 
 		unlock_dir(lower_parent_dentry);
--- a/fs/unionfs/rename.c
+++ b/fs/unionfs/rename.c
@@ -29,6 +29,7 @@ static int __unionfs_rename(struct inode
 	struct dentry *lower_old_dir_dentry;
 	struct dentry *lower_new_dir_dentry;
 	struct dentry *lower_wh_dentry;
+	struct vfsmount *lower_wh_mnt;
 	struct dentry *lower_wh_dir_dentry;
 	char *wh_name = NULL;
 
@@ -61,6 +62,7 @@ static int __unionfs_rename(struct inode
 	lower_wh_dentry = lookup_one_len(wh_name, lower_new_dentry->d_parent,
 					 new_dentry->d_name.len +
 					 UNIONFS_WHLEN);
+	lower_wh_mnt = lower_new_mnt;
 	if (IS_ERR(lower_wh_dentry)) {
 		err = PTR_ERR(lower_wh_dentry);
 		goto out;
@@ -81,7 +83,7 @@ static int __unionfs_rename(struct inode
 		err = is_robranch_super(old_dentry->d_sb, bindex);
 		if (!err)
 			err = vfs_unlink(lower_wh_dir_dentry->d_inode,
-					 lower_wh_dentry);
+					 lower_wh_dentry, lower_wh_mnt);
 
 		dput(lower_wh_dentry);
 		unlock_dir(lower_wh_dir_dentry);
@@ -194,16 +196,18 @@ static int do_unionfs_rename(struct inod
 	for (bindex = old_bstart - 1; bindex >= new_bstart; bindex--) {
 		struct dentry *unlink_dentry;
 		struct dentry *unlink_dir_dentry;
+		struct vfsmount *unlink_mnt;
 
 		unlink_dentry = unionfs_lower_dentry_idx(new_dentry, bindex);
 		if (!unlink_dentry)
 			continue;
 
+		unlink_mnt = unionfs_lower_mnt_idx(new_dentry, bindex);
 		unlink_dir_dentry = lock_parent(unlink_dentry);
 		err = is_robranch_super(old_dir->i_sb, bindex);
 		if (!err)
 			err = vfs_unlink(unlink_dir_dentry->d_inode,
-					 unlink_dentry);
+					 unlink_dentry, unlink_mnt);
 
 		fsstack_copy_attr_times(new_dentry->d_parent->d_inode,
 					unlink_dir_dentry->d_inode);
--- a/fs/unionfs/sioq.c
+++ b/fs/unionfs/sioq.c
@@ -97,7 +97,7 @@ void __unionfs_unlink(struct work_struct
 	struct sioq_args *args = container_of(work, struct sioq_args, work);
 	struct unlink_args *u = &args->unlink;
 
-	args->err = vfs_unlink(u->parent, u->dentry);
+	args->err = vfs_unlink(u->parent, u->dentry, u->mnt);
 	complete(&args->comp);
 }
 
--- a/fs/unionfs/sioq.h
+++ b/fs/unionfs/sioq.h
@@ -58,6 +58,7 @@ struct symlink_args {
 struct unlink_args {
 	struct inode *parent;
 	struct dentry *dentry;
+	struct vfsmount *mnt;
 };
 
 
--- a/fs/unionfs/unlink.c
+++ b/fs/unionfs/unlink.c
@@ -22,6 +22,7 @@
 static int unionfs_unlink_whiteout(struct inode *dir, struct dentry *dentry)
 {
 	struct dentry *lower_dentry;
+	struct vfsmount *lower_mnt;
 	struct dentry *lower_dir_dentry;
 	int bindex;
 	int err = 0;
@@ -36,13 +37,15 @@ static int unionfs_unlink_whiteout(struc
 	if (!lower_dentry)
 		goto out;
 
+	lower_mnt = unionfs_lower_mnt_idx(dentry, bindex);
 	lower_dir_dentry = lock_parent(lower_dentry);
 
 	/* avoid destroying the lower inode if the file is in use */
 	dget(lower_dentry);
 	err = is_robranch_super(dentry->d_sb, bindex);
 	if (!err)
-		err = vfs_unlink(lower_dir_dentry->d_inode, lower_dentry);
+		err = vfs_unlink(lower_dir_dentry->d_inode, lower_dentry,
+				 lower_mnt);
 	/* if vfs_unlink succeeded, update our inode's times */
 	if (!err)
 		unionfs_copy_attr_times(dentry->d_inode);
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1075,7 +1075,7 @@ extern int vfs_mknod(struct inode *, str
 extern int vfs_symlink(struct inode *, struct dentry *, struct vfsmount *, const char *, int);
 extern int vfs_link(struct dentry *, struct vfsmount *, struct inode *, struct dentry *, struct vfsmount *);
 extern int vfs_rmdir(struct inode *, struct dentry *, struct vfsmount *);
-extern int vfs_unlink(struct inode *, struct dentry *);
+extern int vfs_unlink(struct inode *, struct dentry *, struct vfsmount *);
 extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
 
 /*
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -749,7 +749,7 @@ asmlinkage long sys_mq_unlink(const char
 	err = mnt_want_write(mqueue_mnt);
 	if (err)
 		goto out_err;
-	err = vfs_unlink(dentry->d_parent->d_inode, dentry);
+	err = vfs_unlink(dentry->d_parent->d_inode, dentry, mqueue_mnt);
 	mnt_drop_write(mqueue_mnt);
 out_err:
 	dput(dentry);

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 18/47] Pass struct vfsmount to the inode_unlink LSM hook
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (16 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 17/47] Add a struct vfsmount parameter to vfs_unlink() John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 19/47] Add struct vfsmount parameters to vfs_rename() John, Johansen
                   ` (28 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Tony Jones,
	Andreas Gruenbacher, John Johansen

[-- Attachment #1: security-unlink.diff --]
[-- Type: text/plain, Size: 4084 bytes --]

This is needed for computing pathnames in the AppArmor LSM.

Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/namei.c               |    2 +-
 include/linux/security.h |   10 +++++++---
 security/dummy.c         |    3 ++-
 security/security.c      |    5 +++--
 security/selinux/hooks.c |    5 +++--
 5 files changed, 16 insertions(+), 9 deletions(-)

--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2304,7 +2304,7 @@ int vfs_unlink(struct inode *dir, struct
 	if (d_mountpoint(dentry))
 		error = -EBUSY;
 	else {
-		error = security_inode_unlink(dir, dentry);
+		error = security_inode_unlink(dir, dentry, mnt);
 		if (!error)
 			error = dir->i_op->unlink(dir, dentry);
 	}
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -329,6 +329,7 @@ struct request_sock;
  *	Check the permission to remove a hard link to a file. 
  *	@dir contains the inode structure of parent directory of the file.
  *	@dentry contains the dentry structure for file to be unlinked.
+ *	@mnt is the vfsmount corresponding to @dentry (may be NULL).
  *	Return 0 if permission is granted.
  * @inode_symlink:
  *	Check the permission to create a symbolic link to a file.
@@ -1286,7 +1287,8 @@ struct security_operations {
 	int (*inode_link) (struct dentry *old_dentry, struct vfsmount *old_mnt,
 	                   struct inode *dir, struct dentry *new_dentry,
 			   struct vfsmount *new_mnt);
-	int (*inode_unlink) (struct inode *dir, struct dentry *dentry);
+	int (*inode_unlink) (struct inode *dir, struct dentry *dentry,
+			     struct vfsmount *mnt);
 	int (*inode_symlink) (struct inode *dir, struct dentry *dentry,
 			      struct vfsmount *mnt, const char *old_name);
 	int (*inode_mkdir) (struct inode *dir, struct dentry *dentry,
@@ -1554,7 +1556,8 @@ int security_inode_create(struct inode *
 int security_inode_link(struct dentry *old_dentry, struct vfsmount *old_mnt,
 			struct inode *dir, struct dentry *new_dentry,
 			struct vfsmount *new_mnt);
-int security_inode_unlink(struct inode *dir, struct dentry *dentry);
+int security_inode_unlink(struct inode *dir, struct dentry *dentry,
+			  struct vfsmount *mnt);
 int security_inode_symlink(struct inode *dir, struct dentry *dentry,
 			   struct vfsmount *mnt, const char *old_name);
 int security_inode_mkdir(struct inode *dir, struct dentry *dentry,
@@ -1882,7 +1885,8 @@ static inline int security_inode_link (s
 }
 
 static inline int security_inode_unlink (struct inode *dir,
-					 struct dentry *dentry)
+					 struct dentry *dentry,
+					 struct vfsmount *mnt)
 {
 	return 0;
 }
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -303,7 +303,8 @@ static int dummy_inode_link (struct dent
 	return 0;
 }
 
-static int dummy_inode_unlink (struct inode *inode, struct dentry *dentry)
+static int dummy_inode_unlink (struct inode *inode, struct dentry *dentry,
+			       struct vfsmount *mnt)
 {
 	return 0;
 }
--- a/security/security.c
+++ b/security/security.c
@@ -366,11 +366,12 @@ int security_inode_link(struct dentry *o
 					 new_dentry, new_mnt);
 }
 
-int security_inode_unlink(struct inode *dir, struct dentry *dentry)
+int security_inode_unlink(struct inode *dir, struct dentry *dentry,
+			  struct vfsmount *mnt)
 {
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 		return 0;
-	return security_ops->inode_unlink(dir, dentry);
+	return security_ops->inode_unlink(dir, dentry, mnt);
 }
 
 int security_inode_symlink(struct inode *dir, struct dentry *dentry,
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2432,11 +2432,12 @@ static int selinux_inode_link(struct den
 	return may_link(dir, old_dentry, MAY_LINK);
 }
 
-static int selinux_inode_unlink(struct inode *dir, struct dentry *dentry)
+static int selinux_inode_unlink(struct inode *dir, struct dentry *dentry,
+			        struct vfsmount *mnt)
 {
 	int rc;
 
-	rc = secondary_ops->inode_unlink(dir, dentry);
+	rc = secondary_ops->inode_unlink(dir, dentry, mnt);
 	if (rc)
 		return rc;
 	return may_link(dir, dentry, MAY_UNLINK);

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 19/47] Add struct vfsmount parameters to vfs_rename()
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (17 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 18/47] Pass struct vfsmount to the inode_unlink LSM hook John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 20/47] Pass struct vfsmount to the inode_rename LSM hook John, Johansen
                   ` (27 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Tony Jones,
	Andreas Gruenbacher, John Johansen

[-- Attachment #1: vfs-rename.diff --]
[-- Type: text/plain, Size: 5867 bytes --]

The vfsmount will be passed down to the LSM hook so that LSMs can compute
pathnames.

Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/ecryptfs/inode.c |    7 ++++++-
 fs/namei.c          |   19 ++++++++++++-------
 fs/nfsd/vfs.c       |    3 ++-
 fs/unionfs/rename.c |    6 +++++-
 include/linux/fs.h  |    2 +-
 5 files changed, 26 insertions(+), 11 deletions(-)

--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -588,19 +588,24 @@ ecryptfs_rename(struct inode *old_dir, s
 {
 	int rc;
 	struct dentry *lower_old_dentry;
+	struct vfsmount *lower_old_mnt;
 	struct dentry *lower_new_dentry;
+	struct vfsmount *lower_new_mnt;
 	struct dentry *lower_old_dir_dentry;
 	struct dentry *lower_new_dir_dentry;
 
 	lower_old_dentry = ecryptfs_dentry_to_lower(old_dentry);
+	lower_old_mnt = ecryptfs_dentry_to_lower_mnt(old_dentry);
 	lower_new_dentry = ecryptfs_dentry_to_lower(new_dentry);
+	lower_new_mnt = ecryptfs_dentry_to_lower_mnt(new_dentry);
 	dget(lower_old_dentry);
 	dget(lower_new_dentry);
 	lower_old_dir_dentry = dget_parent(lower_old_dentry);
 	lower_new_dir_dentry = dget_parent(lower_new_dentry);
 	lock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
 	rc = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry,
-			lower_new_dir_dentry->d_inode, lower_new_dentry);
+			lower_old_mnt, lower_new_dir_dentry->d_inode,
+			lower_new_dentry, lower_new_mnt);
 	if (rc)
 		goto out_lock;
 	fsstack_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode);
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2600,7 +2600,8 @@ asmlinkage long sys_link(const char __us
  *	   locking].
  */
 static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
-			  struct inode *new_dir, struct dentry *new_dentry)
+			  struct vfsmount *old_mnt, struct inode *new_dir,
+			  struct dentry *new_dentry, struct vfsmount *new_mnt)
 {
 	int error = 0;
 	struct inode *target;
@@ -2643,7 +2644,8 @@ static int vfs_rename_dir(struct inode *
 }
 
 static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
-			    struct inode *new_dir, struct dentry *new_dentry)
+			    struct vfsmount *old_mnt, struct inode *new_dir,
+			    struct dentry *new_dentry, struct vfsmount *new_mnt)
 {
 	struct inode *target;
 	int error;
@@ -2671,7 +2673,8 @@ static int vfs_rename_other(struct inode
 }
 
 int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
-	       struct inode *new_dir, struct dentry *new_dentry)
+	        struct vfsmount *old_mnt, struct inode *new_dir,
+	        struct dentry *new_dentry, struct vfsmount *new_mnt)
 {
 	int error;
 	int is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
@@ -2700,9 +2703,11 @@ int vfs_rename(struct inode *old_dir, st
 	old_name = fsnotify_oldname_init(old_dentry->d_name.name);
 
 	if (is_dir)
-		error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry);
+		error = vfs_rename_dir(old_dir, old_dentry, old_mnt,
+				       new_dir, new_dentry, new_mnt);
 	else
-		error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry);
+		error = vfs_rename_other(old_dir, old_dentry, old_mnt,
+					 new_dir, new_dentry, new_mnt);
 	if (!error) {
 		const char *new_name = old_dentry->d_name.name;
 		fsnotify_move(old_dir, new_dir, old_name, new_name, is_dir,
@@ -2777,8 +2782,8 @@ static int do_rename(int olddfd, const c
 	error = mnt_want_write(oldnd.path.mnt);
 	if (error)
 		goto exit5;
-	error = vfs_rename(old_dir->d_inode, old_dentry,
-				   new_dir->d_inode, new_dentry);
+	error = vfs_rename(old_dir->d_inode, old_dentry, oldnd.path.mnt,
+			   new_dir->d_inode, new_dentry, newnd.path.mnt);
 	mnt_drop_write(oldnd.path.mnt);
 exit5:
 	dput(new_dentry);
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1697,7 +1697,8 @@ nfsd_rename(struct svc_rqst *rqstp, stru
 	if (host_err)
 		goto out_dput_new;
 
-	host_err = vfs_rename(fdir, odentry, tdir, ndentry);
+	host_err = vfs_rename(fdir, odentry, ffhp->fh_export->ex_path.mnt,
+			      tdir, ndentry, tfhp->fh_export->ex_path.mnt);
 	if (!host_err && EX_ISSYNC(tfhp->fh_export)) {
 		host_err = nfsd_sync_dir(tdentry);
 		if (!host_err)
--- a/fs/unionfs/rename.c
+++ b/fs/unionfs/rename.c
@@ -24,6 +24,7 @@ static int __unionfs_rename(struct inode
 {
 	int err = 0;
 	struct dentry *lower_old_dentry;
+	struct vfsmount *lower_old_mnt;
 	struct dentry *lower_new_dentry;
 	struct vfsmount *lower_new_mnt;
 	struct dentry *lower_old_dir_dentry;
@@ -34,7 +35,9 @@ static int __unionfs_rename(struct inode
 	char *wh_name = NULL;
 
 	lower_new_dentry = unionfs_lower_dentry_idx(new_dentry, bindex);
+	lower_new_mnt = unionfs_lower_mnt_idx(new_dentry, bindex);
 	lower_old_dentry = unionfs_lower_dentry_idx(old_dentry, bindex);
+	lower_old_mnt = unionfs_lower_mnt_idx(old_dentry, bindex);
 
 	if (!lower_new_dentry) {
 		lower_new_dentry =
@@ -126,7 +129,8 @@ static int __unionfs_rename(struct inode
 	}
 
 	err = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry,
-			 lower_new_dir_dentry->d_inode, lower_new_dentry);
+			 lower_old_mnt, lower_new_dir_dentry->d_inode,
+			 lower_new_dentry, lower_new_mnt);
 
 out_unlock:
 	unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1076,7 +1076,7 @@ extern int vfs_symlink(struct inode *, s
 extern int vfs_link(struct dentry *, struct vfsmount *, struct inode *, struct dentry *, struct vfsmount *);
 extern int vfs_rmdir(struct inode *, struct dentry *, struct vfsmount *);
 extern int vfs_unlink(struct inode *, struct dentry *, struct vfsmount *);
-extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
+extern int vfs_rename(struct inode *, struct dentry *, struct vfsmount *, struct inode *, struct dentry *, struct vfsmount *);
 
 /*
  * VFS dentry helper functions.

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 20/47] Pass struct vfsmount to the inode_rename LSM hook
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (18 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 19/47] Add struct vfsmount parameters to vfs_rename() John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 21/47] Add a struct vfsmount parameter to vfs_setxattr() John, Johansen
                   ` (26 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Tony Jones,
	Andreas Gruenbacher, John Johansen

[-- Attachment #1: security-rename.diff --]
[-- Type: text/plain, Size: 5385 bytes --]

This is needed for computing pathnames in the AppArmor LSM.

Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/namei.c               |    6 ++++--
 include/linux/security.h |   13 ++++++++++---
 security/dummy.c         |    4 +++-
 security/security.c      |    7 ++++---
 security/selinux/hooks.c |    8 ++++++--
 5 files changed, 27 insertions(+), 11 deletions(-)

--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2616,7 +2616,8 @@ static int vfs_rename_dir(struct inode *
 			return error;
 	}
 
-	error = security_inode_rename(old_dir, old_dentry, new_dir, new_dentry);
+	error = security_inode_rename(old_dir, old_dentry, old_mnt,
+				      new_dir, new_dentry, new_mnt);
 	if (error)
 		return error;
 
@@ -2650,7 +2651,8 @@ static int vfs_rename_other(struct inode
 	struct inode *target;
 	int error;
 
-	error = security_inode_rename(old_dir, old_dentry, new_dir, new_dentry);
+	error = security_inode_rename(old_dir, old_dentry, old_mnt,
+				      new_dir, new_dentry, new_mnt);
 	if (error)
 		return error;
 
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -367,8 +367,10 @@ struct request_sock;
  *	Check for permission to rename a file or directory.
  *	@old_dir contains the inode structure for parent of the old link.
  *	@old_dentry contains the dentry structure of the old link.
+ *	@old_mnt is the vfsmount corresponding to @old_dentry (may be NULL).
  *	@new_dir contains the inode structure for parent of the new link.
  *	@new_dentry contains the dentry structure of the new link.
+ *	@new_mnt is the vfsmount corresponding to @new_dentry (may be NULL).
  *	Return 0 if permission is granted.
  * @inode_readlink:
  *	Check the permission to read the symbolic link.
@@ -1298,7 +1300,9 @@ struct security_operations {
 	int (*inode_mknod) (struct inode *dir, struct dentry *dentry,
 			    struct vfsmount *mnt, int mode, dev_t dev);
 	int (*inode_rename) (struct inode *old_dir, struct dentry *old_dentry,
-	                     struct inode *new_dir, struct dentry *new_dentry);
+			     struct vfsmount *old_mnt,
+	                     struct inode *new_dir, struct dentry *new_dentry,
+			     struct vfsmount *new_mnt);
 	int (*inode_readlink) (struct dentry *dentry, struct vfsmount *mnt);
 	int (*inode_follow_link) (struct dentry *dentry, struct nameidata *nd);
 	int (*inode_permission) (struct inode *inode, int mask, struct nameidata *nd);
@@ -1567,7 +1571,8 @@ int security_inode_rmdir(struct inode *d
 int security_inode_mknod(struct inode *dir, struct dentry *dentry,
 			 struct vfsmount *mnt, int mode, dev_t dev);
 int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
-			   struct inode *new_dir, struct dentry *new_dentry);
+			  struct vfsmount *old_mnt, struct inode *new_dir,
+			  struct dentry *new_dentry, struct vfsmount *new_mnt);
 int security_inode_readlink(struct dentry *dentry, struct vfsmount *mnt);
 int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd);
 int security_inode_permission(struct inode *inode, int mask, struct nameidata *nd);
@@ -1924,8 +1929,10 @@ static inline int security_inode_mknod (
 
 static inline int security_inode_rename (struct inode *old_dir,
 					 struct dentry *old_dentry,
+					 struct vfsmount *old_mnt,
 					 struct inode *new_dir,
-					 struct dentry *new_dentry)
+					 struct dentry *new_dentry,
+					 struct vfsmount *new_mnt)
 {
 	return 0;
 }
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -335,8 +335,10 @@ static int dummy_inode_mknod (struct ino
 
 static int dummy_inode_rename (struct inode *old_inode,
 			       struct dentry *old_dentry,
+			       struct vfsmount *old_mnt,
 			       struct inode *new_inode,
-			       struct dentry *new_dentry)
+			       struct dentry *new_dentry,
+			       struct vfsmount *new_mnt)
 {
 	return 0;
 }
--- a/security/security.c
+++ b/security/security.c
@@ -407,13 +407,14 @@ int security_inode_mknod(struct inode *d
 }
 
 int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
-			   struct inode *new_dir, struct dentry *new_dentry)
+			  struct vfsmount *old_mnt, struct inode *new_dir,
+			  struct dentry *new_dentry, struct vfsmount *new_mnt)
 {
         if (unlikely(IS_PRIVATE(old_dentry->d_inode) ||
             (new_dentry->d_inode && IS_PRIVATE(new_dentry->d_inode))))
 		return 0;
-	return security_ops->inode_rename(old_dir, old_dentry,
-					   new_dir, new_dentry);
+	return security_ops->inode_rename(old_dir, old_dentry, old_mnt,
+					   new_dir, new_dentry, new_mnt);
 }
 
 int security_inode_readlink(struct dentry *dentry, struct vfsmount *mnt)
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2473,8 +2473,12 @@ static int selinux_inode_mknod(struct in
 	return may_create(dir, dentry, inode_mode_to_security_class(mode));
 }
 
-static int selinux_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
-                                struct inode *new_inode, struct dentry *new_dentry)
+static int selinux_inode_rename(struct inode *old_inode,
+				struct dentry *old_dentry,
+				struct vfsmount *old_mnt,
+                                struct inode *new_inode,
+				struct dentry *new_dentry,
+				struct vfsmount *new_mnt)
 {
 	return may_rename(old_inode, old_dentry, new_inode, new_dentry);
 }

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 21/47] Add a struct vfsmount parameter to vfs_setxattr()
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (19 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 20/47] Pass struct vfsmount to the inode_rename LSM hook John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 22/47] Pass struct vfsmount to the inode_setxattr LSM hook John, Johansen
                   ` (25 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Tony Jones,
	Andreas Gruenbacher, John Johansen

[-- Attachment #1: vfs-setxattr.diff --]
[-- Type: text/plain, Size: 7869 bytes --]

The vfsmount will be passed down to the LSM hook so that LSMs can compute
pathnames.

Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/nfsd/vfs.c         |   16 +++++++++++-----
 fs/unionfs/copyup.c   |   18 ++++++++++++------
 fs/unionfs/xattr.c    |    7 +++++--
 fs/xattr.c            |   16 ++++++++--------
 include/linux/xattr.h |    3 ++-
 5 files changed, 38 insertions(+), 22 deletions(-)

--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -426,7 +426,8 @@ static ssize_t nfsd_getxattr(struct dent
 
 #if defined(CONFIG_NFSD_V4)
 static int
-set_nfsv4_acl_one(struct dentry *dentry, struct posix_acl *pacl, char *key)
+set_nfsv4_acl_one(struct dentry *dentry, struct vfsmount *mnt,
+		  struct posix_acl *pacl, char *key)
 {
 	int len;
 	size_t buflen;
@@ -445,7 +446,7 @@ set_nfsv4_acl_one(struct dentry *dentry,
 		goto out;
 	}
 
-	error = vfs_setxattr(dentry, key, buf, len, 0);
+	error = vfs_setxattr(dentry, mnt, key, buf, len, 0);
 out:
 	kfree(buf);
 	return error;
@@ -458,6 +459,7 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqst
 	__be32 error;
 	int host_error;
 	struct dentry *dentry;
+	struct vfsmount *mnt;
 	struct inode *inode;
 	struct posix_acl *pacl = NULL, *dpacl = NULL;
 	unsigned int flags = 0;
@@ -468,6 +470,7 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqst
 		return error;
 
 	dentry = fhp->fh_dentry;
+	mnt = fhp->fh_export->ex_path.mnt;
 	inode = dentry->d_inode;
 	if (S_ISDIR(inode->i_mode))
 		flags = NFS4_ACL_DIR;
@@ -478,12 +481,14 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqst
 	} else if (host_error < 0)
 		goto out_nfserr;
 
-	host_error = set_nfsv4_acl_one(dentry, pacl, POSIX_ACL_XATTR_ACCESS);
+	host_error = set_nfsv4_acl_one(dentry, mnt, pacl,
+				       POSIX_ACL_XATTR_ACCESS);
 	if (host_error < 0)
 		goto out_release;
 
 	if (S_ISDIR(inode->i_mode))
-		host_error = set_nfsv4_acl_one(dentry, dpacl, POSIX_ACL_XATTR_DEFAULT);
+		host_error = set_nfsv4_acl_one(dentry, mnt, dpacl,
+					       POSIX_ACL_XATTR_DEFAULT);
 
 out_release:
 	posix_acl_release(pacl);
@@ -2057,7 +2062,8 @@ nfsd_set_posix_acl(struct svc_fh *fhp, i
 		size = 0;
 
 	if (size)
-		error = vfs_setxattr(fhp->fh_dentry, name, value, size, 0);
+		error = vfs_setxattr(fhp->fh_dentry, fhp->fh_export->ex_path.mnt,
+				     name, value, size,0);
 	else {
 		if (!S_ISDIR(inode->i_mode) && type == ACL_TYPE_DEFAULT)
 			error = 0;
--- a/fs/unionfs/copyup.c
+++ b/fs/unionfs/copyup.c
@@ -26,7 +26,9 @@
 #ifdef CONFIG_UNION_FS_XATTR
 /* copyup all extended attrs for a given dentry */
 static int copyup_xattrs(struct dentry *old_lower_dentry,
-			 struct dentry *new_lower_dentry)
+			 struct vfsmount *old_lower_mnt,
+			 struct dentry *new_lower_dentry,
+			 struct vfsmount *new_lower_mnt)
 {
 	int err = 0;
 	ssize_t list_size = -1;
@@ -82,8 +84,8 @@ static int copyup_xattrs(struct dentry *
 			goto out;
 		}
 		/* Don't lock here since vfs_setxattr does it for us. */
-		err = vfs_setxattr(new_lower_dentry, name_list, attr_value,
-				   size, 0);
+		err = vfs_setxattr(new_lower_dentry, new_lower_mnt, name_list,
+				   attr_value, size, 0);
 		/*
 		 * Selinux depends on "security.*" xattrs, so to maintain
 		 * the security of copied-up files, if Selinux is active,
@@ -93,8 +95,8 @@ static int copyup_xattrs(struct dentry *
 		 */
 		if (err == -EPERM && !capable(CAP_FOWNER)) {
 			cap_raise(current->cap_effective, CAP_FOWNER);
-			err = vfs_setxattr(new_lower_dentry, name_list,
-					   attr_value, size, 0);
+			err = vfs_setxattr(new_lower_dentry, new_lower_mnt,
+					   name_list, attr_value, size, 0);
 			cap_lower(current->cap_effective, CAP_FOWNER);
 		}
 		if (err < 0)
@@ -380,6 +382,7 @@ int copyup_dentry(struct inode *dir, str
 	struct dentry *new_lower_dentry;
 	struct vfsmount *new_lower_mnt;
 	struct dentry *old_lower_dentry = NULL;
+	struct vfsmount *old_lower_mnt;
 	struct super_block *sb;
 	int err = 0;
 	int old_bindex;
@@ -413,6 +416,8 @@ int copyup_dentry(struct inode *dir, str
 	}
 
 	old_lower_dentry = unionfs_lower_dentry_idx(dentry, old_bindex);
+	old_lower_mnt = unionfs_lower_mnt_idx(dentry, old_bindex);
+
 	/* we conditionally dput this old_lower_dentry at end of function */
 	dget(old_lower_dentry);
 
@@ -474,7 +479,8 @@ int copyup_dentry(struct inode *dir, str
 
 #ifdef CONFIG_UNION_FS_XATTR
 	/* Selinux uses extended attributes for permissions. */
-	err = copyup_xattrs(old_lower_dentry, new_lower_dentry);
+	err = copyup_xattrs(old_lower_dentry, old_lower_mnt, 
+			    new_lower_dentry, new_lower_mnt);
 	if (err)
 		goto out_unlink;
 #endif /* CONFIG_UNION_FS_XATTR */
--- a/fs/unionfs/xattr.c
+++ b/fs/unionfs/xattr.c
@@ -72,6 +72,8 @@ int unionfs_setxattr(struct dentry *dent
 		     const void *value, size_t size, int flags)
 {
 	struct dentry *lower_dentry = NULL;
+	struct vfsmount *lower_mnt;
+
 	int err = -EOPNOTSUPP;
 
 	unionfs_read_lock(dentry->d_sb);
@@ -83,9 +85,10 @@ int unionfs_setxattr(struct dentry *dent
 	}
 
 	lower_dentry = unionfs_lower_dentry(dentry);
+	lower_mnt = unionfs_lower_mnt(dentry);
 
-	err = vfs_setxattr(lower_dentry, (char *) name, (void *) value,
-			   size, flags);
+	err = vfs_setxattr(lower_dentry, lower_mnt, (char *) name,
+			   (void *) value, size, flags);
 
 out:
 	unionfs_check_dentry(dentry);
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -67,8 +67,8 @@ xattr_permission(struct inode *inode, co
 }
 
 int
-vfs_setxattr(struct dentry *dentry, char *name, void *value,
-		size_t size, int flags)
+vfs_setxattr(struct dentry *dentry, struct vfsmount *mnt, char *name,
+	     void *value, size_t size, int flags)
 {
 	struct inode *inode = dentry->d_inode;
 	int error;
@@ -218,8 +218,8 @@ EXPORT_SYMBOL_GPL(vfs_removexattr);
  * Extended attribute SET operations
  */
 static long
-setxattr(struct dentry *d, char __user *name, void __user *value,
-	 size_t size, int flags)
+setxattr(struct dentry *dentry, struct vfsmount *mnt, char __user *name,
+	 void __user *value, size_t size, int flags)
 {
 	int error;
 	void *kvalue = NULL;
@@ -246,7 +246,7 @@ setxattr(struct dentry *d, char __user *
 		}
 	}
 
-	error = vfs_setxattr(d, kname, kvalue, size, flags);
+	error = vfs_setxattr(dentry, mnt, kname, kvalue, size, flags);
 	kfree(kvalue);
 	return error;
 }
@@ -264,7 +264,7 @@ sys_setxattr(char __user *path, char __u
 	error = mnt_want_write(nd.path.mnt);
 	if (error)
 		return error;
-	error = setxattr(nd.path.dentry, name, value, size, flags);
+	error = setxattr(nd.path.dentry, nd.path.mnt, name, value, size, flags);
 	mnt_drop_write(nd.path.mnt);
 	path_put(&nd.path);
 	return error;
@@ -283,7 +283,7 @@ sys_lsetxattr(char __user *path, char __
 	error = mnt_want_write(nd.path.mnt);
 	if (error)
 		return error;
-	error = setxattr(nd.path.dentry, name, value, size, flags);
+	error = setxattr(nd.path.dentry, nd.path.mnt, name, value, size, flags);
 	mnt_drop_write(nd.path.mnt);
 	path_put(&nd.path);
 	return error;
@@ -305,7 +305,7 @@ sys_fsetxattr(int fd, char __user *name,
 		goto out_fput;
 	dentry = f->f_path.dentry;
 	audit_inode(NULL, dentry);
-	error = setxattr(dentry, name, value, size, flags);
+	error = setxattr(dentry, f->f_vfsmnt, name, value, size, flags);
 	mnt_drop_write(f->f_vfsmnt);
 out_fput:
 	fput(f);
--- a/include/linux/xattr.h
+++ b/include/linux/xattr.h
@@ -49,7 +49,8 @@ struct xattr_handler {
 ssize_t xattr_getsecurity(struct inode *, const char *, void *, size_t);
 ssize_t vfs_getxattr(struct dentry *, char *, void *, size_t);
 ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size);
-int vfs_setxattr(struct dentry *, char *, void *, size_t, int);
+int vfs_setxattr(struct dentry *, struct vfsmount *, char *, void *, size_t,
+		 int);
 int vfs_removexattr(struct dentry *, char *);
 
 ssize_t generic_getxattr(struct dentry *dentry, const char *name, void *buffer, size_t size);

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 22/47] Pass struct vfsmount to the inode_setxattr LSM hook
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (20 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 21/47] Add a struct vfsmount parameter to vfs_setxattr() John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 23/47] Add a struct vfsmount parameter to vfs_getxattr() John, Johansen
                   ` (24 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Tony Jones,
	Andreas Gruenbacher, John Johansen

[-- Attachment #1: security-setxattr.diff --]
[-- Type: text/plain, Size: 8539 bytes --]

This is needed for computing pathnames in the AppArmor LSM.

Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/xattr.c               |    4 ++--
 include/linux/security.h |   35 +++++++++++++++++++++--------------
 security/commoncap.c     |    4 ++--
 security/dummy.c         |    9 ++++++---
 security/security.c      |   14 ++++++++------
 security/selinux/hooks.c |    8 ++++++--
 6 files changed, 45 insertions(+), 29 deletions(-)

--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -78,7 +78,7 @@ vfs_setxattr(struct dentry *dentry, stru
 		return error;
 
 	mutex_lock(&inode->i_mutex);
-	error = security_inode_setxattr(dentry, name, value, size, flags);
+	error = security_inode_setxattr(dentry, mnt, name, value, size, flags);
 	if (error)
 		goto out;
 	error = -EOPNOTSUPP;
@@ -86,7 +86,7 @@ vfs_setxattr(struct dentry *dentry, stru
 		error = inode->i_op->setxattr(dentry, name, value, size, flags);
 		if (!error) {
 			fsnotify_xattr(dentry);
-			security_inode_post_setxattr(dentry, name, value,
+			security_inode_post_setxattr(dentry, mnt, name, value,
 						     size, flags);
 		}
 	} else if (!strncmp(name, XATTR_SECURITY_PREFIX,
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -57,7 +57,7 @@ extern void cap_capset_set (struct task_
 extern int cap_bprm_set_security (struct linux_binprm *bprm);
 extern void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe);
 extern int cap_bprm_secureexec(struct linux_binprm *bprm);
-extern int cap_inode_setxattr(struct dentry *dentry, char *name, void *value, size_t size, int flags);
+extern int cap_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, char *name, void *value, size_t size, int flags);
 extern int cap_inode_removexattr(struct dentry *dentry, char *name);
 extern int cap_inode_need_killpriv(struct dentry *dentry);
 extern int cap_inode_killpriv(struct dentry *dentry);
@@ -415,11 +415,11 @@ struct request_sock;
  *	inode.
  * @inode_setxattr:
  * 	Check permission before setting the extended attributes
- * 	@value identified by @name for @dentry.
+ * 	@value identified by @name for @dentry and @mnt.
  * 	Return 0 if permission is granted.
  * @inode_post_setxattr:
  * 	Update inode security field after successful setxattr operation.
- * 	@value identified by @name for @dentry.
+ * 	@value identified by @name for @dentry and @mnt.
  * @inode_getxattr:
  * 	Check permission before obtaining the extended attributes
  * 	identified by @name for @dentry.
@@ -1310,9 +1310,11 @@ struct security_operations {
 			      struct iattr *attr);
 	int (*inode_getattr) (struct vfsmount *mnt, struct dentry *dentry);
         void (*inode_delete) (struct inode *inode);
-	int (*inode_setxattr) (struct dentry *dentry, char *name, void *value,
-			       size_t size, int flags);
-	void (*inode_post_setxattr) (struct dentry *dentry, char *name, void *value,
+	int (*inode_setxattr) (struct dentry *dentry, struct vfsmount *mnt,
+			       char *name, void *value, size_t size, int flags);
+	void (*inode_post_setxattr) (struct dentry *dentry,
+				     struct vfsmount *mnt,
+				     char *name, void *value,
 				     size_t size, int flags);
 	int (*inode_getxattr) (struct dentry *dentry, char *name);
 	int (*inode_listxattr) (struct dentry *dentry);
@@ -1580,10 +1582,11 @@ int security_inode_setattr(struct dentry
 			   struct iattr *attr);
 int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry);
 void security_inode_delete(struct inode *inode);
-int security_inode_setxattr(struct dentry *dentry, char *name,
-			     void *value, size_t size, int flags);
-void security_inode_post_setxattr(struct dentry *dentry, char *name,
-				   void *value, size_t size, int flags);
+int security_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt,
+			    char *name, void *value, size_t size, int flags);
+void security_inode_post_setxattr(struct dentry *dentry, struct vfsmount *mnt,
+				  char *name, void *value, size_t size,
+				  int flags);
 int security_inode_getxattr(struct dentry *dentry, char *name);
 int security_inode_listxattr(struct dentry *dentry);
 int security_inode_removexattr(struct dentry *dentry, char *name);
@@ -1971,14 +1974,18 @@ static inline int security_inode_getattr
 static inline void security_inode_delete (struct inode *inode)
 { }
 
-static inline int security_inode_setxattr (struct dentry *dentry, char *name,
+static inline int security_inode_setxattr (struct dentry *dentry,
+					   struct vfsmount *mnt, char *name,
 					   void *value, size_t size, int flags)
 {
-	return cap_inode_setxattr(dentry, name, value, size, flags);
+	return cap_inode_setxattr(dentry, mnt, name, value, size, flags);
 }
 
-static inline void security_inode_post_setxattr (struct dentry *dentry, char *name,
-						 void *value, size_t size, int flags)
+static inline void security_inode_post_setxattr (struct dentry *dentry,
+						 struct vfsmount *mnt,
+						 char *name,
+						 void *value, size_t size,
+						 int flags)
 { }
 
 static inline int security_inode_getxattr (struct dentry *dentry, char *name)
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -379,8 +379,8 @@ int cap_bprm_secureexec (struct linux_bi
 		current->egid != current->gid);
 }
 
-int cap_inode_setxattr(struct dentry *dentry, char *name, void *value,
-		       size_t size, int flags)
+int cap_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, char *name,
+		       void *value, size_t size, int flags)
 {
 	if (!strcmp(name, XATTR_NAME_CAPS)) {
 		if (!capable(CAP_SETFCAP))
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -375,8 +375,9 @@ static void dummy_inode_delete (struct i
 	return;
 }
 
-static int dummy_inode_setxattr (struct dentry *dentry, char *name, void *value,
-				size_t size, int flags)
+static int dummy_inode_setxattr (struct dentry *dentry, struct vfsmount *mnt,
+				 char *name, void *value, size_t size,
+				 int flags)
 {
 	if (!strncmp(name, XATTR_SECURITY_PREFIX,
 		     sizeof(XATTR_SECURITY_PREFIX) - 1) &&
@@ -385,7 +386,9 @@ static int dummy_inode_setxattr (struct 
 	return 0;
 }
 
-static void dummy_inode_post_setxattr (struct dentry *dentry, char *name, void *value,
+static void dummy_inode_post_setxattr (struct dentry *dentry,
+				       struct vfsmount *mnt,
+				       char *name, void *value,
 				       size_t size, int flags)
 {
 }
--- a/security/security.c
+++ b/security/security.c
@@ -460,20 +460,22 @@ void security_inode_delete(struct inode 
 	security_ops->inode_delete(inode);
 }
 
-int security_inode_setxattr(struct dentry *dentry, char *name,
-			     void *value, size_t size, int flags)
+int security_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt,
+			     char *name, void *value, size_t size, int flags)
 {
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 		return 0;
-	return security_ops->inode_setxattr(dentry, name, value, size, flags);
+	return security_ops->inode_setxattr(dentry, mnt, name, value, size,
+					    flags);
 }
 
-void security_inode_post_setxattr(struct dentry *dentry, char *name,
-				   void *value, size_t size, int flags)
+void security_inode_post_setxattr(struct dentry *dentry, struct vfsmount *mnt,
+				  char *name, void *value, size_t size,
+				  int flags)
 {
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 		return;
-	security_ops->inode_post_setxattr(dentry, name, value, size, flags);
+	security_ops->inode_post_setxattr(dentry, mnt, name, value, size, flags);
 }
 
 int security_inode_getxattr(struct dentry *dentry, char *name)
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2559,7 +2559,9 @@ static int selinux_inode_setotherxattr(s
 	return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
 }
 
-static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value, size_t size, int flags)
+static int selinux_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt,
+				  char *name, void *value, size_t size,
+				  int flags)
 {
 	struct task_security_struct *tsec = current->security;
 	struct inode *inode = dentry->d_inode;
@@ -2608,7 +2610,9 @@ static int selinux_inode_setxattr(struct
 			    &ad);
 }
 
-static void selinux_inode_post_setxattr(struct dentry *dentry, char *name,
+static void selinux_inode_post_setxattr(struct dentry *dentry,
+					struct vfsmount *mnt,
+					char *name,
                                         void *value, size_t size, int flags)
 {
 	struct inode *inode = dentry->d_inode;

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 23/47] Add a struct vfsmount parameter to vfs_getxattr()
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (21 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 22/47] Pass struct vfsmount to the inode_setxattr LSM hook John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 24/47] Pass struct vfsmount to the inode_getxattr LSM hook John, Johansen
                   ` (23 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Tony Jones,
	Andreas Gruenbacher, John Johansen

[-- Attachment #1: vfs-getxattr.diff --]
[-- Type: text/plain, Size: 7400 bytes --]

The vfsmount will be passed down to the LSM hook so that LSMs can compute
pathnames.

Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/nfsd/nfs4xdr.c         |    2 +-
 fs/nfsd/vfs.c             |   21 ++++++++++++---------
 fs/unionfs/copyup.c       |    2 +-
 fs/unionfs/xattr.c        |    4 +++-
 fs/xattr.c                |   14 ++++++++------
 include/linux/nfsd/nfsd.h |    3 ++-
 include/linux/xattr.h     |    2 +-
 7 files changed, 28 insertions(+), 20 deletions(-)

--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -1501,7 +1501,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, s
 	}
 	if (bmval0 & (FATTR4_WORD0_ACL | FATTR4_WORD0_ACLSUPPORT
 			| FATTR4_WORD0_SUPPORTED_ATTRS)) {
-		err = nfsd4_get_nfs4_acl(rqstp, dentry, &acl);
+		err = nfsd4_get_nfs4_acl(rqstp, dentry, exp->ex_path.mnt, &acl);
 		aclsupport = (err == 0);
 		if (bmval0 & FATTR4_WORD0_ACL) {
 			if (err == -EOPNOTSUPP)
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -408,11 +408,12 @@ out_nfserr:
 #if defined(CONFIG_NFSD_V2_ACL) || \
     defined(CONFIG_NFSD_V3_ACL) || \
     defined(CONFIG_NFSD_V4)
-static ssize_t nfsd_getxattr(struct dentry *dentry, char *key, void **buf)
+static ssize_t nfsd_getxattr(struct dentry *dentry, struct vfsmount *mnt,
+			     char *key, void **buf)
 {
 	ssize_t buflen;
 
-	buflen = vfs_getxattr(dentry, key, NULL, 0);
+	buflen = vfs_getxattr(dentry, mnt, key, NULL, 0);
 	if (buflen <= 0)
 		return buflen;
 
@@ -420,7 +421,7 @@ static ssize_t nfsd_getxattr(struct dent
 	if (!*buf)
 		return -ENOMEM;
 
-	return vfs_getxattr(dentry, key, *buf, buflen);
+	return vfs_getxattr(dentry, mnt, key, *buf, buflen);
 }
 #endif
 
@@ -501,13 +502,13 @@ out_nfserr:
 }
 
 static struct posix_acl *
-_get_posix_acl(struct dentry *dentry, char *key)
+_get_posix_acl(struct dentry *dentry, struct vfsmount *mnt, char *key)
 {
 	void *buf = NULL;
 	struct posix_acl *pacl = NULL;
 	int buflen;
 
-	buflen = nfsd_getxattr(dentry, key, &buf);
+	buflen = nfsd_getxattr(dentry, mnt, key, &buf);
 	if (!buflen)
 		buflen = -ENODATA;
 	if (buflen <= 0)
@@ -519,14 +520,15 @@ _get_posix_acl(struct dentry *dentry, ch
 }
 
 int
-nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_acl **acl)
+nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
+		   struct vfsmount *mnt, struct nfs4_acl **acl)
 {
 	struct inode *inode = dentry->d_inode;
 	int error = 0;
 	struct posix_acl *pacl = NULL, *dpacl = NULL;
 	unsigned int flags = 0;
 
-	pacl = _get_posix_acl(dentry, POSIX_ACL_XATTR_ACCESS);
+	pacl = _get_posix_acl(dentry, mnt, POSIX_ACL_XATTR_ACCESS);
 	if (IS_ERR(pacl) && PTR_ERR(pacl) == -ENODATA)
 		pacl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
 	if (IS_ERR(pacl)) {
@@ -536,7 +538,7 @@ nfsd4_get_nfs4_acl(struct svc_rqst *rqst
 	}
 
 	if (S_ISDIR(inode->i_mode)) {
-		dpacl = _get_posix_acl(dentry, POSIX_ACL_XATTR_DEFAULT);
+		dpacl = _get_posix_acl(dentry, mnt, POSIX_ACL_XATTR_DEFAULT);
 		if (IS_ERR(dpacl) && PTR_ERR(dpacl) == -ENODATA)
 			dpacl = NULL;
 		else if (IS_ERR(dpacl)) {
@@ -2017,7 +2019,8 @@ nfsd_get_posix_acl(struct svc_fh *fhp, i
 		return ERR_PTR(-EOPNOTSUPP);
 	}
 
-	size = nfsd_getxattr(fhp->fh_dentry, name, &value);
+	size = nfsd_getxattr(fhp->fh_dentry, fhp->fh_export->ex_path.mnt, name,
+			     &value);
 	if (size < 0)
 		return ERR_PTR(size);
 
--- a/fs/unionfs/copyup.c
+++ b/fs/unionfs/copyup.c
@@ -72,7 +72,7 @@ static int copyup_xattrs(struct dentry *
 
 		/* Lock here since vfs_getxattr doesn't lock for us */
 		mutex_lock(&old_lower_dentry->d_inode->i_mutex);
-		size = vfs_getxattr(old_lower_dentry, name_list,
+		size = vfs_getxattr(old_lower_dentry, old_lower_mnt, name_list,
 				    attr_value, XATTR_SIZE_MAX);
 		mutex_unlock(&old_lower_dentry->d_inode->i_mutex);
 		if (size < 0) {
--- a/fs/unionfs/xattr.c
+++ b/fs/unionfs/xattr.c
@@ -43,6 +43,7 @@ ssize_t unionfs_getxattr(struct dentry *
 			 size_t size)
 {
 	struct dentry *lower_dentry = NULL;
+	struct vfsmount *lower_mnt;
 	int err = -EOPNOTSUPP;
 
 	unionfs_read_lock(dentry->d_sb);
@@ -54,8 +55,9 @@ ssize_t unionfs_getxattr(struct dentry *
 	}
 
 	lower_dentry = unionfs_lower_dentry(dentry);
+	lower_mnt = unionfs_lower_mnt(dentry);
 
-	err = vfs_getxattr(lower_dentry, (char *) name, value, size);
+	err = vfs_getxattr(lower_dentry, lower_mnt, (char *) name, value, size);
 
 out:
 	unionfs_check_dentry(dentry);
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -131,7 +131,8 @@ out_noalloc:
 EXPORT_SYMBOL_GPL(xattr_getsecurity);
 
 ssize_t
-vfs_getxattr(struct dentry *dentry, char *name, void *value, size_t size)
+vfs_getxattr(struct dentry *dentry, struct vfsmount *mnt, char *name,
+	     void *value, size_t size)
 {
 	struct inode *inode = dentry->d_inode;
 	int error;
@@ -316,7 +317,8 @@ out_fput:
  * Extended attribute GET operations
  */
 static ssize_t
-getxattr(struct dentry *d, char __user *name, void __user *value, size_t size)
+getxattr(struct dentry *dentry, struct vfsmount *mnt, char __user *name,
+	 void __user *value, size_t size)
 {
 	ssize_t error;
 	void *kvalue = NULL;
@@ -336,7 +338,7 @@ getxattr(struct dentry *d, char __user *
 			return -ENOMEM;
 	}
 
-	error = vfs_getxattr(d, kname, kvalue, size);
+	error = vfs_getxattr(dentry, mnt, kname, kvalue, size);
 	if (error > 0) {
 		if (size && copy_to_user(value, kvalue, error))
 			error = -EFAULT;
@@ -359,7 +361,7 @@ sys_getxattr(char __user *path, char __u
 	error = user_path_walk(path, &nd);
 	if (error)
 		return error;
-	error = getxattr(nd.path.dentry, name, value, size);
+	error = getxattr(nd.path.dentry, nd.path.mnt, name, value, size);
 	path_put(&nd.path);
 	return error;
 }
@@ -374,7 +376,7 @@ sys_lgetxattr(char __user *path, char __
 	error = user_path_walk_link(path, &nd);
 	if (error)
 		return error;
-	error = getxattr(nd.path.dentry, name, value, size);
+	error = getxattr(nd.path.dentry, nd.path.mnt, name, value, size);
 	path_put(&nd.path);
 	return error;
 }
@@ -389,7 +391,7 @@ sys_fgetxattr(int fd, char __user *name,
 	if (!f)
 		return error;
 	audit_inode(NULL, f->f_path.dentry);
-	error = getxattr(f->f_path.dentry, name, value, size);
+	error = getxattr(f->f_path.dentry, f->f_path.mnt, name, value, size);
 	fput(f);
 	return error;
 }
--- a/include/linux/nfsd/nfsd.h
+++ b/include/linux/nfsd/nfsd.h
@@ -78,7 +78,8 @@ __be32		nfsd_setattr(struct svc_rqst *, 
 #ifdef CONFIG_NFSD_V4
 __be32          nfsd4_set_nfs4_acl(struct svc_rqst *, struct svc_fh *,
                     struct nfs4_acl *);
-int             nfsd4_get_nfs4_acl(struct svc_rqst *, struct dentry *, struct nfs4_acl **);
+int             nfsd4_get_nfs4_acl(struct svc_rqst *, struct dentry *,
+				struct vfsmount *mnt, struct nfs4_acl **);
 #endif /* CONFIG_NFSD_V4 */
 __be32		nfsd_create(struct svc_rqst *, struct svc_fh *,
 				char *name, int len, struct iattr *attrs,
--- a/include/linux/xattr.h
+++ b/include/linux/xattr.h
@@ -47,7 +47,7 @@ struct xattr_handler {
 };
 
 ssize_t xattr_getsecurity(struct inode *, const char *, void *, size_t);
-ssize_t vfs_getxattr(struct dentry *, char *, void *, size_t);
+ssize_t vfs_getxattr(struct dentry *, struct vfsmount *, char *, void *, size_t);
 ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size);
 int vfs_setxattr(struct dentry *, struct vfsmount *, char *, void *, size_t,
 		 int);

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 24/47] Pass struct vfsmount to the inode_getxattr LSM hook
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (22 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 23/47] Add a struct vfsmount parameter to vfs_getxattr() John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 25/47] Add a struct vfsmount parameter to vfs_listxattr() John, Johansen
                   ` (22 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Tony Jones,
	Andreas Gruenbacher, John Johansen

[-- Attachment #1: security-getxattr.diff --]
[-- Type: text/plain, Size: 3775 bytes --]

This is needed for computing pathnames in the AppArmor LSM.

Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/xattr.c               |    2 +-
 include/linux/security.h |   11 +++++++----
 security/dummy.c         |    3 ++-
 security/security.c      |    5 +++--
 security/selinux/hooks.c |    3 ++-
 5 files changed, 15 insertions(+), 9 deletions(-)

--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -141,7 +141,7 @@ vfs_getxattr(struct dentry *dentry, stru
 	if (error)
 		return error;
 
-	error = security_inode_getxattr(dentry, name);
+	error = security_inode_getxattr(dentry, mnt, name);
 	if (error)
 		return error;
 
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -422,7 +422,7 @@ struct request_sock;
  * 	@value identified by @name for @dentry and @mnt.
  * @inode_getxattr:
  * 	Check permission before obtaining the extended attributes
- * 	identified by @name for @dentry.
+ * 	identified by @name for @dentry and @mnt.
  * 	Return 0 if permission is granted.
  * @inode_listxattr:
  * 	Check permission before obtaining the list of extended attribute 
@@ -1316,7 +1316,8 @@ struct security_operations {
 				     struct vfsmount *mnt,
 				     char *name, void *value,
 				     size_t size, int flags);
-	int (*inode_getxattr) (struct dentry *dentry, char *name);
+	int (*inode_getxattr) (struct dentry *dentry, struct vfsmount *mnt,
+			       char *name);
 	int (*inode_listxattr) (struct dentry *dentry);
 	int (*inode_removexattr) (struct dentry *dentry, char *name);
 	int (*inode_need_killpriv) (struct dentry *dentry);
@@ -1587,7 +1588,8 @@ int security_inode_setxattr(struct dentr
 void security_inode_post_setxattr(struct dentry *dentry, struct vfsmount *mnt,
 				  char *name, void *value, size_t size,
 				  int flags);
-int security_inode_getxattr(struct dentry *dentry, char *name);
+int security_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt,
+			    char *name);
 int security_inode_listxattr(struct dentry *dentry);
 int security_inode_removexattr(struct dentry *dentry, char *name);
 int security_inode_need_killpriv(struct dentry *dentry);
@@ -1988,7 +1990,8 @@ static inline void security_inode_post_s
 						 int flags)
 { }
 
-static inline int security_inode_getxattr (struct dentry *dentry, char *name)
+static inline int security_inode_getxattr (struct dentry *dentry,
+					    struct vfsmount *mnt, char *name)
 {
 	return 0;
 }
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -393,7 +393,8 @@ static void dummy_inode_post_setxattr (s
 {
 }
 
-static int dummy_inode_getxattr (struct dentry *dentry, char *name)
+static int dummy_inode_getxattr (struct dentry *dentry,
+			          struct vfsmount *mnt, char *name)
 {
 	return 0;
 }
--- a/security/security.c
+++ b/security/security.c
@@ -478,11 +478,12 @@ void security_inode_post_setxattr(struct
 	security_ops->inode_post_setxattr(dentry, mnt, name, value, size, flags);
 }
 
-int security_inode_getxattr(struct dentry *dentry, char *name)
+int security_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt,
+			    char *name)
 {
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 		return 0;
-	return security_ops->inode_getxattr(dentry, name);
+	return security_ops->inode_getxattr(dentry, mnt, name);
 }
 
 int security_inode_listxattr(struct dentry *dentry)
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2636,7 +2636,8 @@ static void selinux_inode_post_setxattr(
 	return;
 }
 
-static int selinux_inode_getxattr (struct dentry *dentry, char *name)
+static int selinux_inode_getxattr (struct dentry *dentry, struct vfsmount *mnt,
+				   char *name)
 {
 	return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
 }

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 25/47] Add a struct vfsmount parameter to vfs_listxattr()
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (23 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 24/47] Pass struct vfsmount to the inode_getxattr LSM hook John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 26/47] Pass struct vfsmount to the inode_listxattr LSM hook John, Johansen
                   ` (21 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Tony Jones,
	Andreas Gruenbacher, John Johansen

[-- Attachment #1: vfs-listxattr.diff --]
[-- Type: text/plain, Size: 4677 bytes --]

The vfsmount will be passed down to the LSM hook so that LSMs can compute
pathnames.

Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/unionfs/copyup.c   |    5 +++--
 fs/unionfs/xattr.c    |    4 +++-
 fs/xattr.c            |   25 ++++++++++++++-----------
 include/linux/xattr.h |    2 +-
 4 files changed, 21 insertions(+), 15 deletions(-)

--- a/fs/unionfs/copyup.c
+++ b/fs/unionfs/copyup.c
@@ -37,7 +37,7 @@ static int copyup_xattrs(struct dentry *
 	char *name_list_buf = NULL;
 
 	/* query the actual size of the xattr list */
-	list_size = vfs_listxattr(old_lower_dentry, NULL, 0);
+	list_size = vfs_listxattr(old_lower_dentry, old_lower_mnt, NULL, 0);
 	if (list_size <= 0) {
 		err = list_size;
 		goto out;
@@ -53,7 +53,8 @@ static int copyup_xattrs(struct dentry *
 	name_list_buf = name_list; /* save for kfree at end */
 
 	/* now get the actual xattr list of the source file */
-	list_size = vfs_listxattr(old_lower_dentry, name_list, list_size);
+	list_size = vfs_listxattr(old_lower_dentry, old_lower_mnt, name_list,
+				  list_size);
 	if (list_size <= 0) {
 		err = list_size;
 		goto out;
--- a/fs/unionfs/xattr.c
+++ b/fs/unionfs/xattr.c
@@ -134,6 +134,7 @@ out:
 ssize_t unionfs_listxattr(struct dentry *dentry, char *list, size_t size)
 {
 	struct dentry *lower_dentry = NULL;
+	struct vfsmount *lower_mnt;
 	int err = -EOPNOTSUPP;
 	char *encoded_list = NULL;
 
@@ -146,9 +147,10 @@ ssize_t unionfs_listxattr(struct dentry 
 	}
 
 	lower_dentry = unionfs_lower_dentry(dentry);
+	lower_mnt = unionfs_lower_mnt(dentry);
 
 	encoded_list = list;
-	err = vfs_listxattr(lower_dentry, encoded_list, size);
+	err = vfs_listxattr(lower_dentry, lower_mnt, encoded_list, size);
 
 out:
 	unionfs_check_dentry(dentry);
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -168,18 +168,20 @@ nolsm:
 EXPORT_SYMBOL_GPL(vfs_getxattr);
 
 ssize_t
-vfs_listxattr(struct dentry *d, char *list, size_t size)
+vfs_listxattr(struct dentry *dentry, struct vfsmount *mnt, char *list,
+	      size_t size)
 {
+	struct inode *inode = dentry->d_inode;
 	ssize_t error;
 
-	error = security_inode_listxattr(d);
+	error = security_inode_listxattr(dentry);
 	if (error)
 		return error;
 	error = -EOPNOTSUPP;
-	if (d->d_inode->i_op && d->d_inode->i_op->listxattr) {
-		error = d->d_inode->i_op->listxattr(d, list, size);
-	} else {
-		error = security_inode_listsecurity(d->d_inode, list, size);
+	if (inode->i_op && inode->i_op->listxattr)
+		error = inode->i_op->listxattr(dentry, list, size);
+	else {
+		error = security_inode_listsecurity(inode, list, size);
 		if (size && error > size)
 			error = -ERANGE;
 	}
@@ -400,7 +402,8 @@ sys_fgetxattr(int fd, char __user *name,
  * Extended attribute LIST operations
  */
 static ssize_t
-listxattr(struct dentry *d, char __user *list, size_t size)
+listxattr(struct dentry *dentry, struct vfsmount *mnt, char __user *list,
+	  size_t size)
 {
 	ssize_t error;
 	char *klist = NULL;
@@ -413,7 +416,7 @@ listxattr(struct dentry *d, char __user 
 			return -ENOMEM;
 	}
 
-	error = vfs_listxattr(d, klist, size);
+	error = vfs_listxattr(dentry, mnt, klist, size);
 	if (error > 0) {
 		if (size && copy_to_user(list, klist, error))
 			error = -EFAULT;
@@ -435,7 +438,7 @@ sys_listxattr(char __user *path, char __
 	error = user_path_walk(path, &nd);
 	if (error)
 		return error;
-	error = listxattr(nd.path.dentry, list, size);
+	error = listxattr(nd.path.dentry, nd.path.mnt, list, size);
 	path_put(&nd.path);
 	return error;
 }
@@ -449,7 +452,7 @@ sys_llistxattr(char __user *path, char _
 	error = user_path_walk_link(path, &nd);
 	if (error)
 		return error;
-	error = listxattr(nd.path.dentry, list, size);
+	error = listxattr(nd.path.dentry, nd.path.mnt, list, size);
 	path_put(&nd.path);
 	return error;
 }
@@ -464,7 +467,7 @@ sys_flistxattr(int fd, char __user *list
 	if (!f)
 		return error;
 	audit_inode(NULL, f->f_path.dentry);
-	error = listxattr(f->f_path.dentry, list, size);
+	error = listxattr(f->f_path.dentry, f->f_path.mnt, list, size);
 	fput(f);
 	return error;
 }
--- a/include/linux/xattr.h
+++ b/include/linux/xattr.h
@@ -48,7 +48,7 @@ struct xattr_handler {
 
 ssize_t xattr_getsecurity(struct inode *, const char *, void *, size_t);
 ssize_t vfs_getxattr(struct dentry *, struct vfsmount *, char *, void *, size_t);
-ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size);
+ssize_t vfs_listxattr(struct dentry *d, struct vfsmount *, char *list, size_t size);
 int vfs_setxattr(struct dentry *, struct vfsmount *, char *, void *, size_t,
 		 int);
 int vfs_removexattr(struct dentry *, char *);

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 26/47] Pass struct vfsmount to the inode_listxattr LSM hook
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (24 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 25/47] Add a struct vfsmount parameter to vfs_listxattr() John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 27/47] Add a struct vfsmount parameter to vfs_removexattr() John, Johansen
                   ` (20 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Tony Jones,
	Andreas Gruenbacher, John Johansen

[-- Attachment #1: security-listxattr.diff --]
[-- Type: text/plain, Size: 3622 bytes --]

This is needed for computing pathnames in the AppArmor LSM.

Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/xattr.c               |    2 +-
 include/linux/security.h |    9 +++++----
 security/dummy.c         |    2 +-
 security/security.c      |    4 ++--
 security/selinux/hooks.c |    2 +-
 5 files changed, 10 insertions(+), 9 deletions(-)

--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -174,7 +174,7 @@ vfs_listxattr(struct dentry *dentry, str
 	struct inode *inode = dentry->d_inode;
 	ssize_t error;
 
-	error = security_inode_listxattr(dentry);
+	error = security_inode_listxattr(dentry, mnt);
 	if (error)
 		return error;
 	error = -EOPNOTSUPP;
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -426,7 +426,7 @@ struct request_sock;
  * 	Return 0 if permission is granted.
  * @inode_listxattr:
  * 	Check permission before obtaining the list of extended attribute 
- * 	names for @dentry.
+ * 	names for @dentry and @mnt.
  * 	Return 0 if permission is granted.
  * @inode_removexattr:
  * 	Check permission before removing the extended attribute
@@ -1318,7 +1318,7 @@ struct security_operations {
 				     size_t size, int flags);
 	int (*inode_getxattr) (struct dentry *dentry, struct vfsmount *mnt,
 			       char *name);
-	int (*inode_listxattr) (struct dentry *dentry);
+	int (*inode_listxattr) (struct dentry *dentry, struct vfsmount *mnt);
 	int (*inode_removexattr) (struct dentry *dentry, char *name);
 	int (*inode_need_killpriv) (struct dentry *dentry);
 	int (*inode_killpriv) (struct dentry *dentry);
@@ -1590,7 +1590,7 @@ void security_inode_post_setxattr(struct
 				  int flags);
 int security_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt,
 			    char *name);
-int security_inode_listxattr(struct dentry *dentry);
+int security_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt);
 int security_inode_removexattr(struct dentry *dentry, char *name);
 int security_inode_need_killpriv(struct dentry *dentry);
 int security_inode_killpriv(struct dentry *dentry);
@@ -1996,7 +1996,8 @@ static inline int security_inode_getxatt
 	return 0;
 }
 
-static inline int security_inode_listxattr (struct dentry *dentry)
+static inline int security_inode_listxattr (struct dentry *dentry,
+					    struct vfsmount *mnt)
 {
 	return 0;
 }
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -399,7 +399,7 @@ static int dummy_inode_getxattr (struct 
 	return 0;
 }
 
-static int dummy_inode_listxattr (struct dentry *dentry)
+static int dummy_inode_listxattr (struct dentry *dentry, struct vfsmount *mnt)
 {
 	return 0;
 }
--- a/security/security.c
+++ b/security/security.c
@@ -486,11 +486,11 @@ int security_inode_getxattr(struct dentr
 	return security_ops->inode_getxattr(dentry, mnt, name);
 }
 
-int security_inode_listxattr(struct dentry *dentry)
+int security_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt)
 {
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 		return 0;
-	return security_ops->inode_listxattr(dentry);
+	return security_ops->inode_listxattr(dentry, mnt);
 }
 
 int security_inode_removexattr(struct dentry *dentry, char *name)
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2642,7 +2642,7 @@ static int selinux_inode_getxattr (struc
 	return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
 }
 
-static int selinux_inode_listxattr (struct dentry *dentry)
+static int selinux_inode_listxattr (struct dentry *dentry, struct vfsmount *mnt)
 {
 	return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
 }

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 27/47] Add a struct vfsmount parameter to vfs_removexattr()
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (25 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 26/47] Pass struct vfsmount to the inode_listxattr LSM hook John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 28/47] Pass struct vfsmount to the inode_removexattr LSM hook John, Johansen
                   ` (19 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Tony Jones,
	Andreas Gruenbacher, John Johansen

[-- Attachment #1: vfs-removexattr.diff --]
[-- Type: text/plain, Size: 4087 bytes --]

The vfsmount will be passed down to the LSM hook so that LSMs can compute
pathnames.

Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/nfsd/vfs.c         |    7 ++++---
 fs/unionfs/xattr.c    |    4 +++-
 fs/xattr.c            |   12 ++++++------
 include/linux/xattr.h |    2 +-
 4 files changed, 14 insertions(+), 11 deletions(-)

--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -2032,6 +2032,7 @@ nfsd_get_posix_acl(struct svc_fh *fhp, i
 int
 nfsd_set_posix_acl(struct svc_fh *fhp, int type, struct posix_acl *acl)
 {
+	struct vfsmount *mnt;
 	struct inode *inode = fhp->fh_dentry->d_inode;
 	char *name;
 	void *value = NULL;
@@ -2064,14 +2065,14 @@ nfsd_set_posix_acl(struct svc_fh *fhp, i
 	} else
 		size = 0;
 
+	mnt = fhp->fh_export->ex_path.mnt;
 	if (size)
-		error = vfs_setxattr(fhp->fh_dentry, fhp->fh_export->ex_path.mnt,
-				     name, value, size,0);
+		error = vfs_setxattr(fhp->fh_dentry, mnt, name, value, size, 0);
 	else {
 		if (!S_ISDIR(inode->i_mode) && type == ACL_TYPE_DEFAULT)
 			error = 0;
 		else {
-			error = vfs_removexattr(fhp->fh_dentry, name);
+			error = vfs_removexattr(fhp->fh_dentry, mnt, name);
 			if (error == -ENODATA)
 				error = 0;
 		}
--- a/fs/unionfs/xattr.c
+++ b/fs/unionfs/xattr.c
@@ -106,6 +106,7 @@ out:
 int unionfs_removexattr(struct dentry *dentry, const char *name)
 {
 	struct dentry *lower_dentry = NULL;
+	struct vfsmount *lower_mnt;
 	int err = -EOPNOTSUPP;
 
 	unionfs_read_lock(dentry->d_sb);
@@ -117,8 +118,9 @@ int unionfs_removexattr(struct dentry *d
 	}
 
 	lower_dentry = unionfs_lower_dentry(dentry);
+	lower_mnt = unionfs_lower_mnt(dentry);
 
-	err = vfs_removexattr(lower_dentry, (char *) name);
+	err = vfs_removexattr(lower_dentry, lower_mnt, (char *) name);
 
 out:
 	unionfs_check_dentry(dentry);
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -190,7 +190,7 @@ vfs_listxattr(struct dentry *dentry, str
 EXPORT_SYMBOL_GPL(vfs_listxattr);
 
 int
-vfs_removexattr(struct dentry *dentry, char *name)
+vfs_removexattr(struct dentry *dentry, struct vfsmount *mnt, char *name)
 {
 	struct inode *inode = dentry->d_inode;
 	int error;
@@ -476,7 +476,7 @@ sys_flistxattr(int fd, char __user *list
  * Extended attribute REMOVE operations
  */
 static long
-removexattr(struct dentry *d, char __user *name)
+removexattr(struct dentry *dentry, struct vfsmount *mnt, char __user *name)
 {
 	int error;
 	char kname[XATTR_NAME_MAX + 1];
@@ -487,7 +487,7 @@ removexattr(struct dentry *d, char __use
 	if (error < 0)
 		return error;
 
-	return vfs_removexattr(d, kname);
+	return vfs_removexattr(dentry, mnt, kname);
 }
 
 asmlinkage long
@@ -499,7 +499,7 @@ sys_removexattr(char __user *path, char 
 	error = user_path_walk(path, &nd);
 	if (error)
 		return error;
-	error = removexattr(nd.path.dentry, name);
+	error = removexattr(nd.path.dentry, nd.path.mnt, name);
 	path_put(&nd.path);
 	return error;
 }
@@ -513,7 +513,7 @@ sys_lremovexattr(char __user *path, char
 	error = user_path_walk_link(path, &nd);
 	if (error)
 		return error;
-	error = removexattr(nd.path.dentry, name);
+	error = removexattr(nd.path.dentry, nd.path.mnt, name);
 	path_put(&nd.path);
 	return error;
 }
@@ -530,7 +530,7 @@ sys_fremovexattr(int fd, char __user *na
 		return error;
 	dentry = f->f_path.dentry;
 	audit_inode(NULL, dentry);
-	error = removexattr(dentry, name);
+	error = removexattr(dentry, f->f_path.mnt, name);
 	fput(f);
 	return error;
 }
--- a/include/linux/xattr.h
+++ b/include/linux/xattr.h
@@ -51,7 +51,7 @@ ssize_t vfs_getxattr(struct dentry *, st
 ssize_t vfs_listxattr(struct dentry *d, struct vfsmount *, char *list, size_t size);
 int vfs_setxattr(struct dentry *, struct vfsmount *, char *, void *, size_t,
 		 int);
-int vfs_removexattr(struct dentry *, char *);
+int vfs_removexattr(struct dentry *, struct vfsmount *, char *);
 
 ssize_t generic_getxattr(struct dentry *dentry, const char *name, void *buffer, size_t size);
 ssize_t generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size);

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 28/47] Pass struct vfsmount to the inode_removexattr LSM hook
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (26 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 27/47] Add a struct vfsmount parameter to vfs_removexattr() John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 29/47] Fix __d_path() for lazy unmounts and make it unambiguous John, Johansen
                   ` (18 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Tony Jones,
	Andreas Gruenbacher, John Johansen

[-- Attachment #1: security-removexattr.diff --]
[-- Type: text/plain, Size: 4973 bytes --]

This is needed for computing pathnames in the AppArmor LSM.

Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/xattr.c               |    2 +-
 include/linux/security.h |   13 ++++++++-----
 security/commoncap.c     |    3 ++-
 security/dummy.c         |    3 ++-
 security/security.c      |    5 +++--
 security/selinux/hooks.c |    3 ++-
 6 files changed, 18 insertions(+), 11 deletions(-)

--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -202,7 +202,7 @@ vfs_removexattr(struct dentry *dentry, s
 	if (error)
 		return error;
 
-	error = security_inode_removexattr(dentry, name);
+	error = security_inode_removexattr(dentry, mnt, name);
 	if (error)
 		return error;
 
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -58,7 +58,7 @@ extern int cap_bprm_set_security (struct
 extern void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe);
 extern int cap_bprm_secureexec(struct linux_binprm *bprm);
 extern int cap_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, char *name, void *value, size_t size, int flags);
-extern int cap_inode_removexattr(struct dentry *dentry, char *name);
+extern int cap_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt, char *name);
 extern int cap_inode_need_killpriv(struct dentry *dentry);
 extern int cap_inode_killpriv(struct dentry *dentry);
 extern int cap_task_post_setuid (uid_t old_ruid, uid_t old_euid, uid_t old_suid, int flags);
@@ -1319,7 +1319,8 @@ struct security_operations {
 	int (*inode_getxattr) (struct dentry *dentry, struct vfsmount *mnt,
 			       char *name);
 	int (*inode_listxattr) (struct dentry *dentry, struct vfsmount *mnt);
-	int (*inode_removexattr) (struct dentry *dentry, char *name);
+	int (*inode_removexattr) (struct dentry *dentry, struct vfsmount *mnt,
+				  char *name);
 	int (*inode_need_killpriv) (struct dentry *dentry);
 	int (*inode_killpriv) (struct dentry *dentry);
 	int (*inode_getsecurity)(const struct inode *inode, const char *name, void **buffer, bool alloc);
@@ -1591,7 +1592,8 @@ void security_inode_post_setxattr(struct
 int security_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt,
 			    char *name);
 int security_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt);
-int security_inode_removexattr(struct dentry *dentry, char *name);
+int security_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt,
+			       char *name);
 int security_inode_need_killpriv(struct dentry *dentry);
 int security_inode_killpriv(struct dentry *dentry);
 int security_inode_getsecurity(const struct inode *inode, const char *name, void **buffer, bool alloc);
@@ -2002,9 +2004,10 @@ static inline int security_inode_listxat
 	return 0;
 }
 
-static inline int security_inode_removexattr (struct dentry *dentry, char *name)
+static inline int security_inode_removexattr (struct dentry *dentry,
+					      struct vfsmount *mnt, char *name)
 {
-	return cap_inode_removexattr(dentry, name);
+	return cap_inode_removexattr(dentry, mnt, name);
 }
 
 static inline int security_inode_need_killpriv(struct dentry *dentry)
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -393,7 +393,8 @@ int cap_inode_setxattr(struct dentry *de
 	return 0;
 }
 
-int cap_inode_removexattr(struct dentry *dentry, char *name)
+int cap_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt,
+			  char *name)
 {
 	if (!strcmp(name, XATTR_NAME_CAPS)) {
 		if (!capable(CAP_SETFCAP))
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -404,7 +404,8 @@ static int dummy_inode_listxattr (struct
 	return 0;
 }
 
-static int dummy_inode_removexattr (struct dentry *dentry, char *name)
+static int dummy_inode_removexattr (struct dentry *dentry, struct vfsmount *mnt,
+				    char *name)
 {
 	if (!strncmp(name, XATTR_SECURITY_PREFIX,
 		     sizeof(XATTR_SECURITY_PREFIX) - 1) &&
--- a/security/security.c
+++ b/security/security.c
@@ -493,11 +493,12 @@ int security_inode_listxattr(struct dent
 	return security_ops->inode_listxattr(dentry, mnt);
 }
 
-int security_inode_removexattr(struct dentry *dentry, char *name)
+int security_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt,
+			       char *name)
 {
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 		return 0;
-	return security_ops->inode_removexattr(dentry, name);
+	return security_ops->inode_removexattr(dentry, mnt, name);
 }
 
 int security_inode_need_killpriv(struct dentry *dentry)
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2647,7 +2647,8 @@ static int selinux_inode_listxattr (stru
 	return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
 }
 
-static int selinux_inode_removexattr (struct dentry *dentry, char *name)
+static int selinux_inode_removexattr (struct dentry *dentry,
+				      struct vfsmount *mnt, char *name)
 {
 	if (strcmp(name, XATTR_NAME_SELINUX))
 		return selinux_inode_setotherxattr(dentry, name);

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 29/47] Fix __d_path() for lazy unmounts and make it unambiguous
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (27 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 28/47] Pass struct vfsmount to the inode_removexattr LSM hook John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 30/47] Make d_path() consistent across mount operations John, Johansen
                   ` (17 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Andreas Gruenbacher,
	John Johansen, Alan Cox

[-- Attachment #1: unambiguous-__d_path.diff --]
[-- Type: text/plain, Size: 7404 bytes --]

First, when __d_path() hits a lazily unmounted mount point, it tries to prepend
the name of the lazily unmounted dentry to the path name.  It gets this wrong,
and also overwrites the slash that separates the name from the following
pathname component. This patch fixes that; if a process was in directory
/foo/bar and /foo got lazily unmounted, the old result was ``foobar'' (note the
missing slash), while the new result with this patch is ``foo/bar''.

Second, it isn't always possible to tell from the __d_path() result whether the
specified root and rootmnt (i.e., the chroot) was reached.  We need an
unambiguous result for AppArmor at least though, so we make sure that paths
will only start with a slash if the path leads all the way up to the root.

We also add a @fail_deleted argument, which allows to get rid of some of the
mess in sys_getcwd().

This patch leaves getcwd() and d_path() as they were before for everything
except for bind-mounted directories; for them, it reports ``/foo/bar'' instead
of ``foobar'' in the example described above.

Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
Acked-by: Alan Cox <alan@lxorguk.ukuu.org.uk>

---
 fs/dcache.c |  166 ++++++++++++++++++++++++++++++++++--------------------------
 1 file changed, 96 insertions(+), 70 deletions(-)

--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1748,51 +1748,49 @@ shouldnt_be_hashed:
 }
 
 /**
- * d_path - return the path of a dentry
- * @dentry: dentry to report
- * @vfsmnt: vfsmnt to which the dentry belongs
- * @root: root dentry
- * @rootmnt: vfsmnt to which the root dentry belongs
+ * __d_path - return the path of a dentry
+ * @path: path to report
+ * @root: root path
  * @buffer: buffer to return value in
  * @buflen: buffer length
+ * @fail_deleted: what to return for deleted files
  *
- * Convert a dentry into an ASCII path name. If the entry has been deleted
+ * If @path is not connected to @root, the path returned will be relative
+ * (i.e., it will not start with a slash).
  * the string " (deleted)" is appended. Note that this is ambiguous.
  *
  * Returns the buffer or an error code if the path was too long.
  *
- * "buflen" should be positive. Caller holds the dcache_lock.
+ * Returns the buffer or an error code.
  */
-static char *__d_path(struct dentry *dentry, struct vfsmount *vfsmnt,
-		       struct path *root, char *buffer, int buflen)
+static char *__d_path(struct path *path, struct path *root,
+                     char *buffer, int buflen, int fail_deleted)
 {
-	char * end = buffer+buflen;
-	char * retval;
-	int namelen;
+	int namelen, is_slash;
+	struct dentry *dentry = path->dentry;
+	struct vfsmount *vfsmnt = path->mnt;
+
+	if (buflen < 2)
+		return ERR_PTR(-ENAMETOOLONG);
+	buffer += --buflen;
+	*buffer = '\0';
 
-	*--end = '\0';
-	buflen--;
+	spin_lock(&dcache_lock);
 	if (!IS_ROOT(dentry) && d_unhashed(dentry)) {
-		buflen -= 10;
-		end -= 10;
-		if (buflen < 0)
+		if (fail_deleted) {
+			buffer = ERR_PTR(-ENOENT);
+			goto out;
+		}
+		if (buflen < 10)
 			goto Elong;
-		memcpy(end, " (deleted)", 10);
+		buflen -= 10;
+		buffer -= 10;
+		memcpy(buffer, " (deleted)", 10);
 	}
-
-	if (buflen < 1)
-		goto Elong;
-	/* Get '/' right */
-	retval = end-1;
-	*retval = '/';
-
-	for (;;) {
+	while (dentry != root->dentry || vfsmnt != root->mnt) {
 		struct dentry * parent;
 
-		if (dentry == root->dentry && vfsmnt == root->mnt)
-			break;
 		if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
-			/* Global root? */
 			spin_lock(&vfsmount_lock);
 			if (vfsmnt->mnt_parent == vfsmnt) {
 				spin_unlock(&vfsmount_lock);
@@ -1806,28 +1804,67 @@ static char *__d_path(struct dentry *den
 		parent = dentry->d_parent;
 		prefetch(parent);
 		namelen = dentry->d_name.len;
-		buflen -= namelen + 1;
-		if (buflen < 0)
+		if (buflen < namelen + 1)
 			goto Elong;
-		end -= namelen;
-		memcpy(end, dentry->d_name.name, namelen);
-		*--end = '/';
-		retval = end;
+		buflen -= namelen + 1;
+		buffer -= namelen;
+		memcpy(buffer, dentry->d_name.name, namelen);
+		*--buffer = '/';
 		dentry = parent;
 	}
+	/* Get '/' right. */
+	if (*buffer != '/')
+		*--buffer = '/';
 
-	return retval;
+out:
+	spin_unlock(&dcache_lock);
+	return buffer;
 
 global_root:
+	/*
+	 * We went past the (vfsmount, dentry) we were looking for and have
+	 * either hit a root dentry, a lazily unmounted dentry, an
+	 * unconnected dentry, or the file is on a pseudo filesystem.
+	 */
 	namelen = dentry->d_name.len;
-	buflen -= namelen;
-	if (buflen < 0)
+	is_slash = (namelen == 1 && *dentry->d_name.name == '/');
+	if (is_slash || (dentry->d_sb->s_flags & MS_NOUSER)) {
+		/*
+		 * Make sure we won't return a pathname starting with '/'.
+		 *
+		 * Historically, we also glue together the root dentry and
+		 * remaining name for pseudo filesystems like pipefs, which
+		 * have the MS_NOUSER flag set. This results in pathnames
+		 * like "pipe:[439336]".
+		 */
+		if (*buffer == '/') {
+			buffer++;
+			buflen++;
+		}
+		if (is_slash)
+			goto out;
+	}
+	if (buflen < namelen)
 		goto Elong;
-	retval -= namelen-1;	/* hit the slash */
-	memcpy(retval, dentry->d_name.name, namelen);
-	return retval;
+	buffer -= namelen;
+	memcpy(buffer, dentry->d_name.name, namelen);
+	goto out;
+
 Elong:
-	return ERR_PTR(-ENAMETOOLONG);
+       buffer = ERR_PTR(-ENAMETOOLONG);
+       goto out;
+}
+
+static char *__connect_d_path(char *path, char *buffer)
+{
+	if (!IS_ERR(path) && *path != '/') {
+		/* Pretend that disconnected paths are hanging off the root. */
+		if (path == buffer)
+			path = ERR_PTR(-ENAMETOOLONG);
+		else
+			*--path = '/';
+	}
+	return path;
 }
 
 /**
@@ -1862,9 +1899,8 @@ char *d_path(struct path *path, char *bu
 	root = current->fs->root;
 	path_get(&current->fs->root);
 	read_unlock(&current->fs->lock);
-	spin_lock(&dcache_lock);
-	res = __d_path(path->dentry, path->mnt, &root, buf, buflen);
-	spin_unlock(&dcache_lock);
+	res = __d_path(path, &root, buf, buflen, 0);
+	res = __connect_d_path(res, buf);
 	path_put(&root);
 	return res;
 }
@@ -1910,9 +1946,9 @@ char *dynamic_dname(struct dentry *dentr
  */
 asmlinkage long sys_getcwd(char __user *buf, unsigned long size)
 {
-	int error;
+	int error, len;
 	struct path pwd, root;
-	char *page = (char *) __get_free_page(GFP_USER);
+	char *page = (char *) __get_free_page(GFP_USER), *cwd;
 
 	if (!page)
 		return -ENOMEM;
@@ -1924,29 +1960,19 @@ asmlinkage long sys_getcwd(char __user *
 	path_get(&current->fs->root);
 	read_unlock(&current->fs->lock);
 
-	error = -ENOENT;
-	/* Has the current directory has been unlinked? */
-	spin_lock(&dcache_lock);
-	if (pwd.dentry->d_parent == pwd.dentry || !d_unhashed(pwd.dentry)) {
-		unsigned long len;
-		char * cwd;
-
-		cwd = __d_path(pwd.dentry, pwd.mnt, &root, page, PAGE_SIZE);
-		spin_unlock(&dcache_lock);
-
-		error = PTR_ERR(cwd);
-		if (IS_ERR(cwd))
-			goto out;
-
-		error = -ERANGE;
-		len = PAGE_SIZE + page - cwd;
-		if (len <= size) {
-			error = len;
-			if (copy_to_user(buf, cwd, len))
-				error = -EFAULT;
-		}
-	} else
-		spin_unlock(&dcache_lock);
+	cwd = __d_path(&pwd, &root, page, PAGE_SIZE, 1);
+	cwd = __connect_d_path(cwd, page);
+	error = PTR_ERR(cwd);
+	if (IS_ERR(cwd))
+		goto out;
+
+	error = -ERANGE;
+	len = PAGE_SIZE + page - cwd;
+	if (len <= size) {
+		error = len;
+		if (copy_to_user(buf, cwd, len))
+			error = -EFAULT;
+	}
 
 out:
 	path_put(&pwd);

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 30/47] Make d_path() consistent across mount operations
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (28 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 29/47] Fix __d_path() for lazy unmounts and make it unambiguous John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 31/47] Add d_namespace_path() to compute namespace relative pathnames John, Johansen
                   ` (16 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Andreas Gruenbacher,
	John Johansen

[-- Attachment #1: mount-consistent-__d_path.diff --]
[-- Type: text/plain, Size: 1875 bytes --]

The path that __d_path() computes can become slightly inconsistent when it
races with mount operations: it grabs the vfsmount_lock when traversing mount
points but immediately drops it again, only to re-grab it when it reaches the
next mount point.  The result is that the filename computed is not always
consisent, and the file may never have had that name. (This is unlikely, but
still possible.)

Fix this by grabbing the vfsmount_lock when the first mount point is reached,
and holding onto it until the d_cache lookup is completed.

Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/dcache.c |   14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1766,7 +1766,7 @@ shouldnt_be_hashed:
 static char *__d_path(struct path *path, struct path *root,
                      char *buffer, int buflen, int fail_deleted)
 {
-	int namelen, is_slash;
+    int namelen, is_slash, vfsmount_locked = 0;
 	struct dentry *dentry = path->dentry;
 	struct vfsmount *vfsmnt = path->mnt;
 
@@ -1791,14 +1791,14 @@ static char *__d_path(struct path *path,
 		struct dentry * parent;
 
 		if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
-			spin_lock(&vfsmount_lock);
-			if (vfsmnt->mnt_parent == vfsmnt) {
-				spin_unlock(&vfsmount_lock);
-				goto global_root;
+			if (!vfsmount_locked) {
+				spin_lock(&vfsmount_lock);
+				vfsmount_locked = 1;
 			}
+			if (vfsmnt->mnt_parent == vfsmnt)
+				goto global_root;
 			dentry = vfsmnt->mnt_mountpoint;
 			vfsmnt = vfsmnt->mnt_parent;
-			spin_unlock(&vfsmount_lock);
 			continue;
 		}
 		parent = dentry->d_parent;
@@ -1817,6 +1817,8 @@ static char *__d_path(struct path *path,
 		*--buffer = '/';
 
 out:
+	if (vfsmount_locked)
+		spin_unlock(&vfsmount_lock);
 	spin_unlock(&dcache_lock);
 	return buffer;
 

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 31/47] Add d_namespace_path() to compute namespace relative pathnames
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (29 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 30/47] Make d_path() consistent across mount operations John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 32/47] From: Miklos Szeredi <mszeredi@suse.cz> John, Johansen
                   ` (15 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Andreas Gruenbacher,
	John Johansen

[-- Attachment #1: d_namespace_path.diff --]
[-- Type: text/plain, Size: 2854 bytes --]

In AppArmor, we are interested in pathnames relative to the namespace root.
This is the same as d_path() except for the root where the search ends. Add
a function for computing the namespace-relative path.

Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/dcache.c            |    4 ++--
 fs/namespace.c         |   26 ++++++++++++++++++++++++++
 include/linux/dcache.h |    1 +
 include/linux/mount.h  |    3 +++
 4 files changed, 32 insertions(+), 2 deletions(-)

--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1763,8 +1763,8 @@ shouldnt_be_hashed:
  *
  * Returns the buffer or an error code.
  */
-static char *__d_path(struct path *path, struct path *root,
-                     char *buffer, int buflen, int fail_deleted)
+char *__d_path(struct path *path, struct path *root, char *buffer, int buflen,
+	       int fail_deleted)
 {
     int namelen, is_slash, vfsmount_locked = 0;
 	struct dentry *dentry = path->dentry;
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -26,6 +26,7 @@
 #include <linux/security.h>
 #include <linux/mount.h>
 #include <linux/ramfs.h>
+#include <linux/path.h>
 #include <asm/uaccess.h>
 #include <asm/unistd.h>
 #include "pnode.h"
@@ -2167,3 +2168,28 @@ void __put_mnt_ns(struct mnt_namespace *
 	release_mounts(&umount_list);
 	kfree(ns);
 }
+
+char *d_namespace_path(struct path *path, char *buf, int buflen)
+{
+	struct path root = { NULL, NULL };
+	struct vfsmount *rootmnt;
+	char *res;
+
+	read_lock(&current->fs->lock);
+	rootmnt = mntget(current->fs->root.mnt);
+	read_unlock(&current->fs->lock);
+	spin_lock(&vfsmount_lock);
+	if (rootmnt->mnt_ns)
+		root.mnt = mntget(rootmnt->mnt_ns->root);
+	spin_unlock(&vfsmount_lock);
+	mntput(rootmnt);
+	if (root.mnt)
+		root.dentry = dget(root.mnt->mnt_root);
+	res = __d_path(path, &root, buf, buflen, 1);
+	path_put(&root);
+	/* Prevent empty path for lazily unmounted filesystems. */
+	if (!IS_ERR(res) && *res == '\0')
+		*--res = '.';
+	return res;
+}
+EXPORT_SYMBOL(d_namespace_path);
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -302,6 +302,7 @@ extern int d_validate(struct dentry *, s
 extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...);
 
 extern char *d_path(struct path *, char *, int);
+extern char *__d_path(struct path *, struct path *, char *, int, int);
 
 /* Allocation counts.. */
 
--- a/include/linux/mount.h
+++ b/include/linux/mount.h
@@ -16,6 +16,7 @@
 #include <linux/list.h>
 #include <linux/nodemask.h>
 #include <linux/spinlock.h>
+#include <linux/path.h>
 #include <asm/atomic.h>
 
 struct super_block;
@@ -114,5 +115,7 @@ extern void shrink_submounts(struct vfsm
 extern spinlock_t vfsmount_lock;
 extern dev_t name_to_dev_t(char *name);
 
+extern char *d_namespace_path(struct path *, char *, int);
+
 #endif
 #endif /* _LINUX_MOUNT_H */

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 32/47] From: Miklos Szeredi <mszeredi@suse.cz>
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (30 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 31/47] Add d_namespace_path() to compute namespace relative pathnames John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 33/47] VFS: new fsetattr() file operation John, Johansen
                   ` (14 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm; +Cc: linux-kernel, linux-security-module, Miklos Szeredi,
	John Johansen

[-- Attachment #1: fgetattr.diff --]
[-- Type: text/plain, Size: 3994 bytes --]

Add a new file operation: f_op->fgetattr(), that is invoked by
fstat().  Fall back to i_op->getattr() if it is not defined.

We need this because fstat() semantics can in some cases be better
implemented if the filesystem has the open file available.

Let's take the following example: we have a network filesystem, with
the server implemented as an unprivileged userspace process running on
a UNIX system (this is basically what sshfs does).

We want the filesystem to follow the familiar UNIX file semantics as
closely as possible.  If for example we have this sequence of events,
we still would like fstat to work correctly:

 1) file X is opened on client
 2) file X is renamed to Y on server
 3) fstat() is performed on open file descriptor on client

This is only possible if the filesystem server acutally uses fstat()
on a file descriptor obtained when the file was opened.  Which means,
the filesystem client needs a way to get this information from the
VFS.

Even if we assume, that the remote filesystem never changes, it is
difficult to implement open-unlink-fstat semantics correctly in the
client, without having this information.

Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Signed-off-by: John Johansen <jjohansen@suse.de>

---

---
 fs/fuse/file.c     |   13 +++++++++++++
 fs/stat.c          |   29 ++++++++++++++++++++++++++++-
 include/linux/fs.h |    1 +
 3 files changed, 42 insertions(+), 1 deletion(-)

--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -871,6 +871,17 @@ static int fuse_file_flock(struct file *
 	return err;
 }
 
+static int fuse_file_fgetattr(struct file *file, struct kstat *stat)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	struct fuse_conn *fc = get_fuse_conn(inode);
+
+	if (!fuse_allow_task(fc, current))
+		return -EACCES;
+
+	return fuse_update_attributes(inode, stat, file, NULL);
+}
+
 static sector_t fuse_bmap(struct address_space *mapping, sector_t block)
 {
 	struct inode *inode = mapping->host;
@@ -920,6 +931,7 @@ static const struct file_operations fuse
 	.fsync		= fuse_fsync,
 	.lock		= fuse_file_lock,
 	.flock		= fuse_file_flock,
+	.fgetattr	= fuse_file_fgetattr,
 	.splice_read	= generic_file_splice_read,
 };
 
@@ -933,6 +945,7 @@ static const struct file_operations fuse
 	.fsync		= fuse_fsync,
 	.lock		= fuse_file_lock,
 	.flock		= fuse_file_flock,
+	.fgetattr	= fuse_file_fgetattr,
 	/* no mmap and splice_read */
 };
 
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -55,6 +55,33 @@ int vfs_getattr(struct vfsmount *mnt, st
 
 EXPORT_SYMBOL(vfs_getattr);
 
+/*
+ * Perform getattr on an open file
+ *
+ * Fall back to i_op->getattr (or generic_fillattr) if the filesystem
+ * doesn't define an f_op->fgetattr operation.
+ */
+static int vfs_fgetattr(struct file *file, struct kstat *stat)
+{
+	struct vfsmount *mnt = file->f_path.mnt;
+	struct dentry *dentry = file->f_path.dentry;
+	struct inode *inode = dentry->d_inode;
+	int retval;
+
+	retval = security_inode_getattr(mnt, dentry);
+	if (retval)
+		return retval;
+
+	if (file->f_op && file->f_op->fgetattr) {
+		return file->f_op->fgetattr(file, stat);
+	} else if (inode->i_op->getattr) {
+		return inode->i_op->getattr(mnt, dentry, stat);
+	} else {
+		generic_fillattr(inode, stat);
+		return 0;
+	}
+}
+
 int vfs_stat_fd(int dfd, char __user *name, struct kstat *stat)
 {
 	struct nameidata nd;
@@ -101,7 +128,7 @@ int vfs_fstat(unsigned int fd, struct ks
 	int error = -EBADF;
 
 	if (f) {
-		error = vfs_getattr(f->f_path.mnt, f->f_path.dentry, stat);
+		error = vfs_fgetattr(f, stat);
 		fput(f);
 	}
 	return error;
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1190,6 +1190,7 @@ struct file_operations {
 	ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
 	ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
 	int (*setlease)(struct file *, long, struct file_lock **);
+	int (*fgetattr)(struct file *, struct kstat *);
 };
 
 struct inode_operations {

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 33/47] VFS: new fsetattr() file operation
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (31 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 32/47] From: Miklos Szeredi <mszeredi@suse.cz> John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 34/47] Pass struct file down the inode_*xattr security LSM hooks John, Johansen
                   ` (13 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm; +Cc: linux-kernel, linux-security-module, Miklos Szeredi,
	John Johansen

[-- Attachment #1: fsetattr.diff --]
[-- Type: text/plain, Size: 20066 bytes --]

From: Miklos Szeredi <mszeredi@suse.cz>

Add a new file operation: f_op->fsetattr(), that is invoked by
ftruncate, fchmod, fchown and utimensat.  Fall back to i_op->setattr()
if it is not defined.

For the reasons why we need this, see patch adding fgetattr().

ftruncate() already passed the open file to the filesystem via the
ia_file member of struct iattr.  However it is cleaner to have a
separate file operation for this, so remove ia_file and convert
existing users: fuse, AFS, and reiserfs4.  Use ATTR_FILE to allow
LSMs to distinguish file descriptor based operations.

Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/afs/dir.c                             |    1 +
 fs/afs/file.c                            |    1 +
 fs/afs/inode.c                           |   19 +++++++++++++++----
 fs/afs/internal.h                        |    1 +
 fs/attr.c                                |   18 ++++++++++++++----
 fs/fuse/dir.c                            |   20 +++++++++-----------
 fs/fuse/file.c                           |    7 +++++++
 fs/fuse/fuse_i.h                         |    4 ++++
 fs/open.c                                |   25 +++++++++++++------------
 fs/reiser4/plugin/file/cryptcompress.c   |   12 +++++++++++-
 fs/reiser4/plugin/file/file.c            |   27 ++++++++++++++++++++-------
 fs/reiser4/plugin/file/file.h            |    3 +++
 fs/reiser4/plugin/file/file_conversion.c |    7 +++++++
 fs/reiser4/plugin/object.c               |    5 ++++-
 fs/reiser4/plugin/plugin.h               |    1 +
 fs/utimes.c                              |    2 +-
 include/linux/fs.h                       |    9 ++-------
 17 files changed, 114 insertions(+), 48 deletions(-)

--- a/fs/afs/dir.c
+++ b/fs/afs/dir.c
@@ -45,6 +45,7 @@ const struct file_operations afs_dir_fil
 	.release	= afs_release,
 	.readdir	= afs_readdir,
 	.lock		= afs_lock,
+	.fsetattr	= afs_fsetattr,
 };
 
 const struct inode_operations afs_dir_inode_operations = {
--- a/fs/afs/file.c
+++ b/fs/afs/file.c
@@ -36,6 +36,7 @@ const struct file_operations afs_file_op
 	.fsync		= afs_fsync,
 	.lock		= afs_lock,
 	.flock		= afs_flock,
+	.fsetattr	= afs_fsetattr,
 };
 
 const struct inode_operations afs_file_inode_operations = {
--- a/fs/afs/inode.c
+++ b/fs/afs/inode.c
@@ -358,7 +358,8 @@ void afs_clear_inode(struct inode *inode
 /*
  * set the attributes of an inode
  */
-int afs_setattr(struct dentry *dentry, struct iattr *attr)
+static int afs_do_setattr(struct dentry *dentry, struct iattr *attr,
+		   struct file *file)
 {
 	struct afs_vnode *vnode = AFS_FS_I(dentry->d_inode);
 	struct key *key;
@@ -380,8 +381,8 @@ int afs_setattr(struct dentry *dentry, s
 		afs_writeback_all(vnode);
 	}
 
-	if (attr->ia_valid & ATTR_FILE) {
-		key = attr->ia_file->private_data;
+	if (file) {
+		key = file->private_data;
 	} else {
 		key = afs_request_key(vnode->volume->cell);
 		if (IS_ERR(key)) {
@@ -391,10 +392,20 @@ int afs_setattr(struct dentry *dentry, s
 	}
 
 	ret = afs_vnode_setattr(vnode, key, attr);
-	if (!(attr->ia_valid & ATTR_FILE))
+	if (!file)
 		key_put(key);
 
 error:
 	_leave(" = %d", ret);
 	return ret;
 }
+
+int afs_setattr(struct dentry *dentry, struct iattr *attr)
+{
+	return afs_do_setattr(dentry, attr, NULL);
+}
+
+int afs_fsetattr(struct file *file, struct iattr *attr)
+{
+	return afs_do_setattr(file->f_path.dentry, attr, file);
+}
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -550,6 +550,7 @@ extern void afs_zap_data(struct afs_vnod
 extern int afs_validate(struct afs_vnode *, struct key *);
 extern int afs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
 extern int afs_setattr(struct dentry *, struct iattr *);
+extern int afs_fsetattr(struct file *, struct iattr *);
 extern void afs_clear_inode(struct inode *);
 
 /*
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -100,8 +100,8 @@ int inode_setattr(struct inode * inode, 
 }
 EXPORT_SYMBOL(inode_setattr);
 
-int notify_change(struct dentry *dentry, struct vfsmount *mnt,
-		  struct iattr *attr)
+int fnotify_change(struct dentry *dentry, struct vfsmount *mnt,
+		   struct iattr *attr, struct file *file)
 {
 	struct inode *inode = dentry->d_inode;
 	mode_t mode = inode->i_mode;
@@ -160,8 +160,12 @@ int notify_change(struct dentry *dentry,
 
 	if (inode->i_op && inode->i_op->setattr) {
 		error = security_inode_setattr(dentry, mnt, attr);
-		if (!error)
-			error = inode->i_op->setattr(dentry, attr);
+		if (!error) {
+			if (file && file->f_op && file->f_op->fsetattr)
+				error = file->f_op->fsetattr(file, attr);
+			else
+				error = inode->i_op->setattr(dentry, attr);
+		}
 	} else {
 		error = inode_change_ok(inode, attr);
 		if (!error)
@@ -184,4 +188,10 @@ int notify_change(struct dentry *dentry,
 	return error;
 }
 
+int notify_change(struct dentry *dentry, struct vfsmount *mnt,
+		  struct iattr *attr)
+{
+	return fnotify_change(dentry, mnt, attr, NULL);
+}
+
 EXPORT_SYMBOL(notify_change);
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1063,21 +1063,22 @@ static int fuse_dir_fsync(struct file *f
 	return file ? fuse_fsync_common(file, de, datasync, 1) : 0;
 }
 
-static bool update_mtime(unsigned ivalid)
+static bool update_mtime(unsigned ivalid, bool have_file)
 {
 	/* Always update if mtime is explicitly set  */
 	if (ivalid & ATTR_MTIME_SET)
 		return true;
 
 	/* If it's an open(O_TRUNC) or an ftruncate(), don't update */
-	if ((ivalid & ATTR_SIZE) && (ivalid & (ATTR_OPEN | ATTR_FILE)))
+	if ((ivalid & ATTR_SIZE) && ((ivalid & ATTR_OPEN) || have_file))
 		return false;
 
 	/* In all other cases update */
 	return true;
 }
 
-static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg)
+static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg,
+			   bool have_file)
 {
 	unsigned ivalid = iattr->ia_valid;
 
@@ -1096,7 +1097,7 @@ static void iattr_to_fattr(struct iattr 
 		if (!(ivalid & ATTR_ATIME_SET))
 			arg->valid |= FATTR_ATIME_NOW;
 	}
-	if ((ivalid & ATTR_MTIME) && update_mtime(ivalid)) {
+	if ((ivalid & ATTR_MTIME) && update_mtime(ivalid, have_file)) {
 		arg->valid |= FATTR_MTIME;
 		arg->mtime = iattr->ia_mtime.tv_sec;
 		arg->mtimensec = iattr->ia_mtime.tv_nsec;
@@ -1113,8 +1114,8 @@ static void iattr_to_fattr(struct iattr 
  * vmtruncate() doesn't allow for this case, so do the rlimit checking
  * and the actual truncation by hand.
  */
-static int fuse_do_setattr(struct dentry *entry, struct iattr *attr,
-			   struct file *file)
+int fuse_do_setattr(struct dentry *entry, struct iattr *attr,
+		    struct file *file)
 {
 	struct inode *inode = entry->d_inode;
 	struct fuse_conn *fc = get_fuse_conn(inode);
@@ -1152,7 +1153,7 @@ static int fuse_do_setattr(struct dentry
 
 	memset(&inarg, 0, sizeof(inarg));
 	memset(&outarg, 0, sizeof(outarg));
-	iattr_to_fattr(attr, &inarg);
+	iattr_to_fattr(attr, &inarg, file != NULL);
 	if (file) {
 		struct fuse_file *ff = file->private_data;
 		inarg.valid |= FATTR_FH;
@@ -1194,10 +1195,7 @@ static int fuse_do_setattr(struct dentry
 
 static int fuse_setattr(struct dentry *entry, struct iattr *attr)
 {
-	if (attr->ia_valid & ATTR_FILE)
-		return fuse_do_setattr(entry, attr, attr->ia_file);
-	else
-		return fuse_do_setattr(entry, attr, NULL);
+	return fuse_do_setattr(entry, attr, NULL);
 }
 
 static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -918,6 +918,11 @@ static sector_t fuse_bmap(struct address
 	return err ? 0 : outarg.block;
 }
 
+static int fuse_fsetattr(struct file *file, struct iattr *attr)
+{
+	return fuse_do_setattr(file->f_path.dentry, attr, file);
+}
+
 static const struct file_operations fuse_file_operations = {
 	.llseek		= generic_file_llseek,
 	.read		= do_sync_read,
@@ -932,6 +937,7 @@ static const struct file_operations fuse
 	.lock		= fuse_file_lock,
 	.flock		= fuse_file_flock,
 	.fgetattr	= fuse_file_fgetattr,
+	.fsetattr	= fuse_fsetattr,
 	.splice_read	= generic_file_splice_read,
 };
 
@@ -946,6 +952,7 @@ static const struct file_operations fuse
 	.lock		= fuse_file_lock,
 	.flock		= fuse_file_flock,
 	.fgetattr	= fuse_file_fgetattr,
+	.fsetattr	= fuse_fsetattr,
 	/* no mmap and splice_read */
 };
 
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -505,6 +505,10 @@ void fuse_change_attributes(struct inode
  */
 int fuse_dev_init(void);
 
+
+int fuse_do_setattr(struct dentry *entry, struct iattr *attr,
+		    struct file *file);
+
 /**
  * Cleanup the client device
  */
--- a/fs/open.c
+++ b/fs/open.c
@@ -206,16 +206,15 @@ int do_truncate(struct dentry *dentry, s
 
 	newattrs.ia_size = length;
 	newattrs.ia_valid = ATTR_SIZE | time_attrs;
-	if (filp) {
-		newattrs.ia_file = filp;
+
+	if (filp)
 		newattrs.ia_valid |= ATTR_FILE;
-	}
 
 	/* Remove suid/sgid on truncate too */
 	newattrs.ia_valid |= should_remove_suid(dentry);
 
 	mutex_lock(&dentry->d_inode->i_mutex);
-	err = notify_change(dentry, mnt, &newattrs);
+	err = fnotify_change(dentry, mnt, &newattrs, filp);
 	mutex_unlock(&dentry->d_inode->i_mutex);
 	return err;
 }
@@ -590,8 +589,8 @@ asmlinkage long sys_fchmod(unsigned int 
 	if (mode == (mode_t) -1)
 		mode = inode->i_mode;
 	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
-	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
-	err = notify_change(dentry, file->f_path.mnt, &newattrs);
+	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME | ATTR_FILE;
+	err = fnotify_change(dentry, file->f_path.mnt, &newattrs, file);
 	mutex_unlock(&inode->i_mutex);
 
 out_drop_write:
@@ -645,7 +644,7 @@ asmlinkage long sys_chmod(const char __u
 }
 
 static int chown_common(struct dentry * dentry, struct vfsmount *mnt,
-			uid_t user, gid_t group)
+			uid_t user, gid_t group, struct file *file)
 {
 	struct inode * inode;
 	int error;
@@ -671,8 +670,10 @@ static int chown_common(struct dentry * 
 	if (!S_ISDIR(inode->i_mode))
 		newattrs.ia_valid |=
 			ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
+	if (file)
+		newattrs.ia_valid |= ATTR_FILE;
 	mutex_lock(&inode->i_mutex);
-	error = notify_change(dentry, mnt, &newattrs);
+	error = fnotify_change(dentry, mnt, &newattrs, file);
 	mutex_unlock(&inode->i_mutex);
 out:
 	return error;
@@ -689,7 +690,7 @@ asmlinkage long sys_chown(const char __u
 	error = mnt_want_write(nd.path.mnt);
 	if (error)
 		goto out_release;
-	error = chown_common(nd.path.dentry, nd.path.mnt, user, group);
+	error = chown_common(nd.path.dentry, nd.path.mnt, user, group, NULL);
 	mnt_drop_write(nd.path.mnt);
 out_release:
 	path_put(&nd.path);
@@ -714,7 +715,7 @@ asmlinkage long sys_fchownat(int dfd, co
 	error = mnt_want_write(nd.path.mnt);
 	if (error)
 		goto out_release;
-	error = chown_common(nd.path.dentry, nd.path.mnt, user, group);
+	error = chown_common(nd.path.dentry, nd.path.mnt, user, group, NULL);
 	mnt_drop_write(nd.path.mnt);
 out_release:
 	path_put(&nd.path);
@@ -733,7 +734,7 @@ asmlinkage long sys_lchown(const char __
 	error = mnt_want_write(nd.path.mnt);
 	if (error)
 		goto out_release;
-	error = chown_common(nd.path.dentry, nd.path.mnt, user, group);
+	error = chown_common(nd.path.dentry, nd.path.mnt, user, group, NULL);
 	mnt_drop_write(nd.path.mnt);
 out_release:
 	path_put(&nd.path);
@@ -757,7 +758,7 @@ asmlinkage long sys_fchown(unsigned int 
 		goto out_fput;
 	dentry = file->f_path.dentry;
 	audit_inode(NULL, dentry);
-	error = chown_common(dentry, file->f_path.mnt, user, group);
+	error = chown_common(dentry, file->f_path.mnt, user, group, file);
 	mnt_drop_write(file->f_vfsmnt);
 out_fput:
 	fput(file);
--- a/fs/reiser4/plugin/file/cryptcompress.c
+++ b/fs/reiser4/plugin/file/cryptcompress.c
@@ -3688,7 +3688,7 @@ int delete_object_cryptcompress(struct i
  * plugin->setattr
  * This implements actual truncate (see comments in reiser4/page_cache.c)
  */
-int setattr_cryptcompress(struct dentry *dentry, struct iattr *attr)
+static int do_setattr_cryptcompress(struct dentry *dentry, struct iattr *attr)
 {
 	int result;
 	struct inode *inode;
@@ -3731,6 +3731,16 @@ int setattr_cryptcompress(struct dentry 
 	return result;
 }
 
+int setattr_cryptcompress(struct dentry *dentry, struct iattr *attr)
+{
+	return do_setattr_cryptcompress(dentry, attr);
+}
+
+int fsetattr_cryptcompress(struct file *file, struct iattr *attr)
+{
+	return do_setattr_cryptcompress(file->f_path.dentry, attr);
+}
+
 /* plugin->release */
 int release_cryptcompress(struct inode *inode, struct file *file)
 {
--- a/fs/reiser4/plugin/file/file.c
+++ b/fs/reiser4/plugin/file/file.c
@@ -561,19 +561,20 @@ static int should_have_notail(const stru
  * truncate_file_body - change length of file
  * @inode: inode of file
  * @new_size: new file length
+ * @file: open file being truncated through foperation
  *
  * Adjusts items file @inode is built of to match @new_size. It may either cut
  * items or add them to represent a hole at the end of file. The caller has to
  * obtain exclusive access to the file.
  */
-static int truncate_file_body(struct inode *inode, struct iattr *attr)
+static int truncate_file_body(struct inode *inode, struct iattr *attr,
+			      struct file *file)
 {
 	int result;
 	loff_t new_size = attr->ia_size;
 
 	if (inode->i_size < new_size) {
 		/* expanding truncate */
-		struct file * file = attr->ia_file;
 		struct unix_file_info *uf_info = unix_file_inode_data(inode);
 
 		assert("edward-1532", attr->ia_valid & ATTR_FILE);
@@ -2581,7 +2582,8 @@ owns_item_unix_file(const struct inode *
 	return 1;
 }
 
-static int setattr_truncate(struct inode *inode, struct iattr *attr)
+static int setattr_truncate(struct inode *inode, struct iattr *attr,
+			    struct file *file)
 {
 	int result;
 	int s_result;
@@ -2597,7 +2599,7 @@ static int setattr_truncate(struct inode
 	if (result == 0)
 		result = safe_link_add(inode, SAFE_TRUNCATE);
 	if (result == 0)
-		result = truncate_file_body(inode, attr);
+		result = truncate_file_body(inode, attr, file);
 	if (result)
 		warning("vs-1588", "truncate_file failed: oid %lli, "
 			"old size %lld, new size %lld, retval %d",
@@ -2619,8 +2621,9 @@ static int setattr_truncate(struct inode
 /* plugin->u.file.setattr method */
 /* This calls inode_setattr and if truncate is in effect it also takes
    exclusive inode access to avoid races */
-int setattr_unix_file(struct dentry *dentry,	/* Object to change attributes */
-		      struct iattr *attr /* change description */ )
+static int do_setattr_unix_file(struct dentry *dentry,	/* Object to change attributes */
+				struct iattr *attr, /* change description */
+				struct file *file)
 {
 	int result;
 
@@ -2636,7 +2639,7 @@ int setattr_unix_file(struct dentry *den
 
 		uf_info = unix_file_inode_data(dentry->d_inode);
 		get_exclusive_access_careful(uf_info, dentry->d_inode);
-		result = setattr_truncate(dentry->d_inode, attr);
+		result = setattr_truncate(dentry->d_inode, attr, file);
 		drop_exclusive_access(uf_info);
 		context_set_commit_async(ctx);
 		reiser4_exit_context(ctx);
@@ -2646,6 +2649,16 @@ int setattr_unix_file(struct dentry *den
 	return result;
 }
 
+int setattr_unix_file(struct dentry *dentry, struct iattr *attr)
+{
+	return do_setattr_unix_file(dentry, attr, NULL);
+}
+
+int fsetattr_unix_file(struct file *file, struct iattr *attr)
+{
+	return do_setattr_unix_file(file->f_path.dentry, attr, file);
+}
+
 /* plugin->u.file.init_inode_data */
 void
 init_inode_data_unix_file(struct inode *inode,
--- a/fs/reiser4/plugin/file/file.h
+++ b/fs/reiser4/plugin/file/file.h
@@ -39,6 +39,7 @@ int reiser4_mmap_careful(struct file *, 
 int reiser4_open_careful(struct inode *inode, struct file *file);
 int reiser4_release_careful(struct inode *, struct file *);
 int reiser4_sync_file_common(struct file *, struct dentry *, int datasync);
+int reiser4_fsetattr_careful(struct file *file, struct iattr *attr);
 
 /* address space operations */
 int reiser4_readpage(struct file *, struct page *);
@@ -70,6 +71,7 @@ int ioctl_unix_file(struct inode *, stru
 int mmap_unix_file(struct file *, struct vm_area_struct *);
 int open_unix_file(struct inode *, struct file *);
 int release_unix_file(struct inode *, struct file *);
+int fsetattr_unix_file(struct file *, struct iattr *);
 
 /* private address space operations */
 int readpage_unix_file(struct file *, struct page *);
@@ -107,6 +109,7 @@ int ioctl_cryptcompress(struct inode *, 
 int mmap_cryptcompress(struct file *, struct vm_area_struct *);
 int open_cryptcompress(struct inode *, struct file *);
 int release_cryptcompress(struct inode *, struct file *);
+int fsetattr_cryptcompress(struct file *file, struct iattr *attr);
 
 /* private address space operations */
 int readpage_cryptcompress(struct file *, struct page *);
--- a/fs/reiser4/plugin/file/file_conversion.c
+++ b/fs/reiser4/plugin/file/file_conversion.c
@@ -602,6 +602,13 @@ int reiser4_setattr_careful(struct dentr
 			   setattr_conversion_hook(inode, attr));
 }
 
+int reiser4_fsetattr_careful(struct file *file, struct iattr *attr)
+{
+	struct inode * inode = file->f_path.dentry->d_inode;
+	return PROT_ACTIVE(int, fsetattr, (file, attr),
+			   setattr_conversion_hook(inode, attr));
+}
+
 /* Wrappers with passive protection for:
  *
  * ->open();
--- a/fs/reiser4/plugin/object.c
+++ b/fs/reiser4/plugin/object.c
@@ -105,7 +105,8 @@ static struct file_operations regular_fi
 	.release = reiser4_release_careful,
 	.fsync = reiser4_sync_file_common,
 	.splice_read = generic_file_splice_read,
-	.splice_write = generic_file_splice_write
+	.splice_write = generic_file_splice_write,
+	.fsetattr = reiser4_fsetattr_careful
 };
 static struct address_space_operations regular_file_a_ops = {
 	.writepage = reiser4_writepage,
@@ -211,6 +212,7 @@ file_plugin file_plugins[LAST_FILE_PLUGI
 		.writepages = writepages_unix_file,
 		.prepare_write = prepare_write_unix_file,
 		.commit_write = commit_write_unix_file,
+		.fsetattr = fsetattr_unix_file,
 		/*
 		 * private a_ops
 		 */
@@ -405,6 +407,7 @@ file_plugin file_plugins[LAST_FILE_PLUGI
 		.writepages = writepages_cryptcompress,
 		.prepare_write = prepare_write_cryptcompress,
 		.commit_write = commit_write_cryptcompress,
+		.fsetattr = fsetattr_cryptcompress,
 
 		.bmap = bmap_cryptcompress,
 
--- a/fs/reiser4/plugin/plugin.h
+++ b/fs/reiser4/plugin/plugin.h
@@ -237,6 +237,7 @@ typedef struct file_plugin {
 		      unsigned int cmd, unsigned long arg);
 	int (*mmap) (struct file *, struct vm_area_struct *);
 	int (*release) (struct inode *, struct file *);
+	int (*fsetattr)(struct file *, struct iattr *);
 	/*
 	 * private a_ops
 	 */
--- a/fs/utimes.c
+++ b/fs/utimes.c
@@ -135,7 +135,7 @@ long do_utimes(int dfd, char __user *fil
 		}
 	}
 	mutex_lock(&inode->i_mutex);
-	error = notify_change(path.dentry, path.mnt, &newattrs);
+	error = fnotify_change(path.dentry, path.mnt, &newattrs, f);
 	mutex_unlock(&inode->i_mutex);
 mnt_drop_write_and_out:
 	mnt_drop_write(path.mnt);
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -351,13 +351,6 @@ struct iattr {
 	struct timespec	ia_atime;
 	struct timespec	ia_mtime;
 	struct timespec	ia_ctime;
-
-	/*
-	 * Not an attribute, but an auxilary info for filesystems wanting to
-	 * implement an ftruncate() like method.  NOTE: filesystem should
-	 * check for (ia_valid & ATTR_FILE), and not for (ia_file != NULL).
-	 */
-	struct file	*ia_file;
 };
 
 /*
@@ -1191,6 +1184,7 @@ struct file_operations {
 	ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
 	int (*setlease)(struct file *, long, struct file_lock **);
 	int (*fgetattr)(struct file *, struct kstat *);
+	int (*fsetattr)(struct file *, struct iattr *);
 };
 
 struct inode_operations {
@@ -1697,6 +1691,7 @@ extern int do_remount_sb(struct super_bl
 extern sector_t bmap(struct inode *, sector_t);
 #endif
 extern int notify_change(struct dentry *, struct vfsmount *, struct iattr *);
+extern int fnotify_change(struct dentry *, struct vfsmount *, struct iattr *, struct file *);
 extern int permission(struct inode *, int, struct nameidata *);
 extern int generic_permission(struct inode *, int,
 		int (*check_acl)(struct inode *, int));

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 34/47] Pass struct file down the inode_*xattr security LSM hooks
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (32 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 33/47] VFS: new fsetattr() file operation John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 35/47] Factor out sysctl pathname code John, Johansen
                   ` (12 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Andreas Gruenbacher,
	John Johansen

[-- Attachment #1: security-xattr-file.diff --]
[-- Type: text/plain, Size: 24078 bytes --]

This allows LSMs to also distinguish between file descriptor and path
access for the xattr operations. (The other relevant operations are
covered by the setattr hook.)

Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/nfsd/vfs.c            |   12 +++++----
 fs/unionfs/copyup.c      |   12 +++++----
 fs/unionfs/xattr.c       |    9 +++----
 fs/xattr.c               |   60 +++++++++++++++++++++++++----------------------
 include/linux/security.h |   40 ++++++++++++++++++-------------
 include/linux/xattr.h    |   10 ++++---
 security/commoncap.c     |    4 +--
 security/dummy.c         |   10 ++++---
 security/security.c      |   21 +++++++++-------
 security/selinux/hooks.c |   10 ++++---
 10 files changed, 107 insertions(+), 81 deletions(-)

--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -413,7 +413,7 @@ static ssize_t nfsd_getxattr(struct dent
 {
 	ssize_t buflen;
 
-	buflen = vfs_getxattr(dentry, mnt, key, NULL, 0);
+	buflen = vfs_getxattr(dentry, mnt, key, NULL, 0, NULL);
 	if (buflen <= 0)
 		return buflen;
 
@@ -421,7 +421,7 @@ static ssize_t nfsd_getxattr(struct dent
 	if (!*buf)
 		return -ENOMEM;
 
-	return vfs_getxattr(dentry, mnt, key, *buf, buflen);
+	return vfs_getxattr(dentry, mnt, key, *buf, buflen, NULL);
 }
 #endif
 
@@ -447,7 +447,7 @@ set_nfsv4_acl_one(struct dentry *dentry,
 		goto out;
 	}
 
-	error = vfs_setxattr(dentry, mnt, key, buf, len, 0);
+	error = vfs_setxattr(dentry, mnt, key, buf, len, 0, NULL);
 out:
 	kfree(buf);
 	return error;
@@ -2067,12 +2067,14 @@ nfsd_set_posix_acl(struct svc_fh *fhp, i
 
 	mnt = fhp->fh_export->ex_path.mnt;
 	if (size)
-		error = vfs_setxattr(fhp->fh_dentry, mnt, name, value, size, 0);
+		error = vfs_setxattr(fhp->fh_dentry, mnt, name, value, size, 0,
+				     NULL);
 	else {
 		if (!S_ISDIR(inode->i_mode) && type == ACL_TYPE_DEFAULT)
 			error = 0;
 		else {
-			error = vfs_removexattr(fhp->fh_dentry, mnt, name);
+			error = vfs_removexattr(fhp->fh_dentry, mnt, name,
+						NULL);
 			if (error == -ENODATA)
 				error = 0;
 		}
--- a/fs/unionfs/copyup.c
+++ b/fs/unionfs/copyup.c
@@ -37,7 +37,8 @@ static int copyup_xattrs(struct dentry *
 	char *name_list_buf = NULL;
 
 	/* query the actual size of the xattr list */
-	list_size = vfs_listxattr(old_lower_dentry, old_lower_mnt, NULL, 0);
+	list_size = vfs_listxattr(old_lower_dentry, old_lower_mnt, NULL, 0,
+				  NULL);
 	if (list_size <= 0) {
 		err = list_size;
 		goto out;
@@ -54,7 +55,7 @@ static int copyup_xattrs(struct dentry *
 
 	/* now get the actual xattr list of the source file */
 	list_size = vfs_listxattr(old_lower_dentry, old_lower_mnt, name_list,
-				  list_size);
+				  list_size, NULL);
 	if (list_size <= 0) {
 		err = list_size;
 		goto out;
@@ -74,7 +75,7 @@ static int copyup_xattrs(struct dentry *
 		/* Lock here since vfs_getxattr doesn't lock for us */
 		mutex_lock(&old_lower_dentry->d_inode->i_mutex);
 		size = vfs_getxattr(old_lower_dentry, old_lower_mnt, name_list,
-				    attr_value, XATTR_SIZE_MAX);
+				    attr_value, XATTR_SIZE_MAX, NULL);
 		mutex_unlock(&old_lower_dentry->d_inode->i_mutex);
 		if (size < 0) {
 			err = size;
@@ -86,7 +87,7 @@ static int copyup_xattrs(struct dentry *
 		}
 		/* Don't lock here since vfs_setxattr does it for us. */
 		err = vfs_setxattr(new_lower_dentry, new_lower_mnt, name_list,
-				   attr_value, size, 0);
+				   attr_value, size, 0, NULL);
 		/*
 		 * Selinux depends on "security.*" xattrs, so to maintain
 		 * the security of copied-up files, if Selinux is active,
@@ -97,7 +98,8 @@ static int copyup_xattrs(struct dentry *
 		if (err == -EPERM && !capable(CAP_FOWNER)) {
 			cap_raise(current->cap_effective, CAP_FOWNER);
 			err = vfs_setxattr(new_lower_dentry, new_lower_mnt,
-					   name_list, attr_value, size, 0);
+					   name_list, attr_value, size, 0,
+					   NULL);
 			cap_lower(current->cap_effective, CAP_FOWNER);
 		}
 		if (err < 0)
--- a/fs/unionfs/xattr.c
+++ b/fs/unionfs/xattr.c
@@ -57,7 +57,8 @@ ssize_t unionfs_getxattr(struct dentry *
 	lower_dentry = unionfs_lower_dentry(dentry);
 	lower_mnt = unionfs_lower_mnt(dentry);
 
-	err = vfs_getxattr(lower_dentry, lower_mnt, (char *) name, value, size);
+	err = vfs_getxattr(lower_dentry, lower_mnt, (char *) name, value, size,
+			   NULL);
 
 out:
 	unionfs_check_dentry(dentry);
@@ -90,7 +91,7 @@ int unionfs_setxattr(struct dentry *dent
 	lower_mnt = unionfs_lower_mnt(dentry);
 
 	err = vfs_setxattr(lower_dentry, lower_mnt, (char *) name,
-			   (void *) value, size, flags);
+			   (void *) value, size, flags, NULL);
 
 out:
 	unionfs_check_dentry(dentry);
@@ -120,7 +121,7 @@ int unionfs_removexattr(struct dentry *d
 	lower_dentry = unionfs_lower_dentry(dentry);
 	lower_mnt = unionfs_lower_mnt(dentry);
 
-	err = vfs_removexattr(lower_dentry, lower_mnt, (char *) name);
+	err = vfs_removexattr(lower_dentry, lower_mnt, (char *) name, NULL);
 
 out:
 	unionfs_check_dentry(dentry);
@@ -152,7 +153,7 @@ ssize_t unionfs_listxattr(struct dentry 
 	lower_mnt = unionfs_lower_mnt(dentry);
 
 	encoded_list = list;
-	err = vfs_listxattr(lower_dentry, lower_mnt, encoded_list, size);
+	err = vfs_listxattr(lower_dentry, lower_mnt, encoded_list, size, NULL);
 
 out:
 	unionfs_check_dentry(dentry);
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -68,7 +68,7 @@ xattr_permission(struct inode *inode, co
 
 int
 vfs_setxattr(struct dentry *dentry, struct vfsmount *mnt, char *name,
-	     void *value, size_t size, int flags)
+	     void *value, size_t size, int flags, struct file *file)
 {
 	struct inode *inode = dentry->d_inode;
 	int error;
@@ -78,7 +78,7 @@ vfs_setxattr(struct dentry *dentry, stru
 		return error;
 
 	mutex_lock(&inode->i_mutex);
-	error = security_inode_setxattr(dentry, mnt, name, value, size, flags);
+	error = security_inode_setxattr(dentry, mnt, name, value, size, flags,						file);
 	if (error)
 		goto out;
 	error = -EOPNOTSUPP;
@@ -132,7 +132,7 @@ EXPORT_SYMBOL_GPL(xattr_getsecurity);
 
 ssize_t
 vfs_getxattr(struct dentry *dentry, struct vfsmount *mnt, char *name,
-	     void *value, size_t size)
+	     void *value, size_t size, struct file *file)
 {
 	struct inode *inode = dentry->d_inode;
 	int error;
@@ -141,7 +141,7 @@ vfs_getxattr(struct dentry *dentry, stru
 	if (error)
 		return error;
 
-	error = security_inode_getxattr(dentry, mnt, name);
+	error = security_inode_getxattr(dentry, mnt, name, file);
 	if (error)
 		return error;
 
@@ -169,12 +169,12 @@ EXPORT_SYMBOL_GPL(vfs_getxattr);
 
 ssize_t
 vfs_listxattr(struct dentry *dentry, struct vfsmount *mnt, char *list,
-	      size_t size)
+	      size_t size, struct file *file)
 {
 	struct inode *inode = dentry->d_inode;
 	ssize_t error;
 
-	error = security_inode_listxattr(dentry, mnt);
+	error = security_inode_listxattr(dentry, mnt, file);
 	if (error)
 		return error;
 	error = -EOPNOTSUPP;
@@ -190,7 +190,8 @@ vfs_listxattr(struct dentry *dentry, str
 EXPORT_SYMBOL_GPL(vfs_listxattr);
 
 int
-vfs_removexattr(struct dentry *dentry, struct vfsmount *mnt, char *name)
+vfs_removexattr(struct dentry *dentry, struct vfsmount *mnt, char *name,
+		struct file *file)
 {
 	struct inode *inode = dentry->d_inode;
 	int error;
@@ -202,7 +203,7 @@ vfs_removexattr(struct dentry *dentry, s
 	if (error)
 		return error;
 
-	error = security_inode_removexattr(dentry, mnt, name);
+	error = security_inode_removexattr(dentry, mnt, name, file);
 	if (error)
 		return error;
 
@@ -222,7 +223,7 @@ EXPORT_SYMBOL_GPL(vfs_removexattr);
  */
 static long
 setxattr(struct dentry *dentry, struct vfsmount *mnt, char __user *name,
-	 void __user *value, size_t size, int flags)
+	 void __user *value, size_t size, int flags, struct file *file)
 {
 	int error;
 	void *kvalue = NULL;
@@ -249,7 +250,7 @@ setxattr(struct dentry *dentry, struct v
 		}
 	}
 
-	error = vfs_setxattr(dentry, mnt, kname, kvalue, size, flags);
+	error = vfs_setxattr(dentry, mnt, kname, kvalue, size, flags, file);
 	kfree(kvalue);
 	return error;
 }
@@ -267,7 +268,8 @@ sys_setxattr(char __user *path, char __u
 	error = mnt_want_write(nd.path.mnt);
 	if (error)
 		return error;
-	error = setxattr(nd.path.dentry, nd.path.mnt, name, value, size, flags);
+	error = setxattr(nd.path.dentry, nd.path.mnt, name, value, size, flags,
+			 NULL);
 	mnt_drop_write(nd.path.mnt);
 	path_put(&nd.path);
 	return error;
@@ -286,7 +288,8 @@ sys_lsetxattr(char __user *path, char __
 	error = mnt_want_write(nd.path.mnt);
 	if (error)
 		return error;
-	error = setxattr(nd.path.dentry, nd.path.mnt, name, value, size, flags);
+	error = setxattr(nd.path.dentry, nd.path.mnt, name, value, size, flags,
+			 NULL);
 	mnt_drop_write(nd.path.mnt);
 	path_put(&nd.path);
 	return error;
@@ -308,7 +311,7 @@ sys_fsetxattr(int fd, char __user *name,
 		goto out_fput;
 	dentry = f->f_path.dentry;
 	audit_inode(NULL, dentry);
-	error = setxattr(dentry, f->f_vfsmnt, name, value, size, flags);
+	error = setxattr(dentry, f->f_vfsmnt, name, value, size, flags, f);
 	mnt_drop_write(f->f_vfsmnt);
 out_fput:
 	fput(f);
@@ -320,7 +323,7 @@ out_fput:
  */
 static ssize_t
 getxattr(struct dentry *dentry, struct vfsmount *mnt, char __user *name,
-	 void __user *value, size_t size)
+	 void __user *value, size_t size, struct file *file)
 {
 	ssize_t error;
 	void *kvalue = NULL;
@@ -340,7 +343,7 @@ getxattr(struct dentry *dentry, struct v
 			return -ENOMEM;
 	}
 
-	error = vfs_getxattr(dentry, mnt, kname, kvalue, size);
+	error = vfs_getxattr(dentry, mnt, kname, kvalue, size, file);
 	if (error > 0) {
 		if (size && copy_to_user(value, kvalue, error))
 			error = -EFAULT;
@@ -363,7 +366,7 @@ sys_getxattr(char __user *path, char __u
 	error = user_path_walk(path, &nd);
 	if (error)
 		return error;
-	error = getxattr(nd.path.dentry, nd.path.mnt, name, value, size);
+	error = getxattr(nd.path.dentry, nd.path.mnt, name, value, size, NULL);
 	path_put(&nd.path);
 	return error;
 }
@@ -378,7 +381,7 @@ sys_lgetxattr(char __user *path, char __
 	error = user_path_walk_link(path, &nd);
 	if (error)
 		return error;
-	error = getxattr(nd.path.dentry, nd.path.mnt, name, value, size);
+	error = getxattr(nd.path.dentry, nd.path.mnt, name, value, size, NULL);
 	path_put(&nd.path);
 	return error;
 }
@@ -393,7 +396,7 @@ sys_fgetxattr(int fd, char __user *name,
 	if (!f)
 		return error;
 	audit_inode(NULL, f->f_path.dentry);
-	error = getxattr(f->f_path.dentry, f->f_path.mnt, name, value, size);
+	error = getxattr(f->f_path.dentry, f->f_path.mnt, name, value, size, f);
 	fput(f);
 	return error;
 }
@@ -403,7 +406,7 @@ sys_fgetxattr(int fd, char __user *name,
  */
 static ssize_t
 listxattr(struct dentry *dentry, struct vfsmount *mnt, char __user *list,
-	  size_t size)
+	  size_t size, struct file *file)
 {
 	ssize_t error;
 	char *klist = NULL;
@@ -416,7 +419,7 @@ listxattr(struct dentry *dentry, struct 
 			return -ENOMEM;
 	}
 
-	error = vfs_listxattr(dentry, mnt, klist, size);
+	error = vfs_listxattr(dentry, mnt, klist, size, file);
 	if (error > 0) {
 		if (size && copy_to_user(list, klist, error))
 			error = -EFAULT;
@@ -438,7 +441,7 @@ sys_listxattr(char __user *path, char __
 	error = user_path_walk(path, &nd);
 	if (error)
 		return error;
-	error = listxattr(nd.path.dentry, nd.path.mnt, list, size);
+	error = listxattr(nd.path.dentry, nd.path.mnt, list, size, NULL);
 	path_put(&nd.path);
 	return error;
 }
@@ -452,7 +455,7 @@ sys_llistxattr(char __user *path, char _
 	error = user_path_walk_link(path, &nd);
 	if (error)
 		return error;
-	error = listxattr(nd.path.dentry, nd.path.mnt, list, size);
+	error = listxattr(nd.path.dentry, nd.path.mnt, list, size, NULL);
 	path_put(&nd.path);
 	return error;
 }
@@ -467,7 +470,7 @@ sys_flistxattr(int fd, char __user *list
 	if (!f)
 		return error;
 	audit_inode(NULL, f->f_path.dentry);
-	error = listxattr(f->f_path.dentry, f->f_path.mnt, list, size);
+	error = listxattr(f->f_path.dentry, f->f_path.mnt, list, size, f);
 	fput(f);
 	return error;
 }
@@ -476,7 +479,8 @@ sys_flistxattr(int fd, char __user *list
  * Extended attribute REMOVE operations
  */
 static long
-removexattr(struct dentry *dentry, struct vfsmount *mnt, char __user *name)
+removexattr(struct dentry *dentry, struct vfsmount *mnt, char __user *name,
+	    struct file *file)
 {
 	int error;
 	char kname[XATTR_NAME_MAX + 1];
@@ -487,7 +491,7 @@ removexattr(struct dentry *dentry, struc
 	if (error < 0)
 		return error;
 
-	return vfs_removexattr(dentry, mnt, kname);
+	return vfs_removexattr(dentry, mnt, kname, file);
 }
 
 asmlinkage long
@@ -499,7 +503,7 @@ sys_removexattr(char __user *path, char 
 	error = user_path_walk(path, &nd);
 	if (error)
 		return error;
-	error = removexattr(nd.path.dentry, nd.path.mnt, name);
+	error = removexattr(nd.path.dentry, nd.path.mnt, name, NULL);
 	path_put(&nd.path);
 	return error;
 }
@@ -513,7 +517,7 @@ sys_lremovexattr(char __user *path, char
 	error = user_path_walk_link(path, &nd);
 	if (error)
 		return error;
-	error = removexattr(nd.path.dentry, nd.path.mnt, name);
+	error = removexattr(nd.path.dentry, nd.path.mnt, name, NULL);
 	path_put(&nd.path);
 	return error;
 }
@@ -530,7 +534,7 @@ sys_fremovexattr(int fd, char __user *na
 		return error;
 	dentry = f->f_path.dentry;
 	audit_inode(NULL, dentry);
-	error = removexattr(dentry, f->f_path.mnt, name);
+	error = removexattr(dentry, f->f_path.mnt, name, f);
 	fput(f);
 	return error;
 }
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -57,8 +57,8 @@ extern void cap_capset_set (struct task_
 extern int cap_bprm_set_security (struct linux_binprm *bprm);
 extern void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe);
 extern int cap_bprm_secureexec(struct linux_binprm *bprm);
-extern int cap_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, char *name, void *value, size_t size, int flags);
-extern int cap_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt, char *name);
+extern int cap_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, char *name, void *value, size_t size, int flags, struct file *file);
+extern int cap_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt, char *name, struct file *file);
 extern int cap_inode_need_killpriv(struct dentry *dentry);
 extern int cap_inode_killpriv(struct dentry *dentry);
 extern int cap_task_post_setuid (uid_t old_ruid, uid_t old_euid, uid_t old_suid, int flags);
@@ -1311,16 +1311,18 @@ struct security_operations {
 	int (*inode_getattr) (struct vfsmount *mnt, struct dentry *dentry);
         void (*inode_delete) (struct inode *inode);
 	int (*inode_setxattr) (struct dentry *dentry, struct vfsmount *mnt,
-			       char *name, void *value, size_t size, int flags);
+			       char *name, void *value, size_t size, int flags,
+			       struct file *file);
 	void (*inode_post_setxattr) (struct dentry *dentry,
 				     struct vfsmount *mnt,
 				     char *name, void *value,
 				     size_t size, int flags);
 	int (*inode_getxattr) (struct dentry *dentry, struct vfsmount *mnt,
-			       char *name);
-	int (*inode_listxattr) (struct dentry *dentry, struct vfsmount *mnt);
+			       char *name, struct file *file);
+	int (*inode_listxattr) (struct dentry *dentry, struct vfsmount *mnt,
+				struct file *file);
 	int (*inode_removexattr) (struct dentry *dentry, struct vfsmount *mnt,
-				  char *name);
+				  char *name, struct file *file);
 	int (*inode_need_killpriv) (struct dentry *dentry);
 	int (*inode_killpriv) (struct dentry *dentry);
 	int (*inode_getsecurity)(const struct inode *inode, const char *name, void **buffer, bool alloc);
@@ -1585,15 +1587,17 @@ int security_inode_setattr(struct dentry
 int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry);
 void security_inode_delete(struct inode *inode);
 int security_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt,
-			    char *name, void *value, size_t size, int flags);
+			    char *name, void *value, size_t size, int flags,
+			    struct file *file);
 void security_inode_post_setxattr(struct dentry *dentry, struct vfsmount *mnt,
 				  char *name, void *value, size_t size,
 				  int flags);
 int security_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt,
-			    char *name);
-int security_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt);
+			    char *name, struct file *file);
+int security_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt,
+			     struct file *file);
 int security_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt,
-			       char *name);
+			       char *name, struct file *file);
 int security_inode_need_killpriv(struct dentry *dentry);
 int security_inode_killpriv(struct dentry *dentry);
 int security_inode_getsecurity(const struct inode *inode, const char *name, void **buffer, bool alloc);
@@ -1980,9 +1984,10 @@ static inline void security_inode_delete
 
 static inline int security_inode_setxattr (struct dentry *dentry,
 					   struct vfsmount *mnt, char *name,
-					   void *value, size_t size, int flags)
+					   void *value, size_t size, int flags,
+					   struct file *file)
 {
-	return cap_inode_setxattr(dentry, mnt, name, value, size, flags);
+	return cap_inode_setxattr(dentry, mnt, name, value, size, flags, file);
 }
 
 static inline void security_inode_post_setxattr (struct dentry *dentry,
@@ -1993,21 +1998,24 @@ static inline void security_inode_post_s
 { }
 
 static inline int security_inode_getxattr (struct dentry *dentry,
-					    struct vfsmount *mnt, char *name)
+					   struct vfsmount *mnt, char *name,
+					   struct file *file)
 {
 	return 0;
 }
 
 static inline int security_inode_listxattr (struct dentry *dentry,
-					    struct vfsmount *mnt)
+					   struct vfsmount *mnt,
+					   struct file *file)
 {
 	return 0;
 }
 
 static inline int security_inode_removexattr (struct dentry *dentry,
-					      struct vfsmount *mnt, char *name)
+					     struct vfsmount *mnt, char *name,
+					     struct file *file)
 {
-	return cap_inode_removexattr(dentry, mnt, name);
+	return cap_inode_removexattr(dentry, mnt, name, file);
 }
 
 static inline int security_inode_need_killpriv(struct dentry *dentry)
--- a/include/linux/xattr.h
+++ b/include/linux/xattr.h
@@ -47,11 +47,13 @@ struct xattr_handler {
 };
 
 ssize_t xattr_getsecurity(struct inode *, const char *, void *, size_t);
-ssize_t vfs_getxattr(struct dentry *, struct vfsmount *, char *, void *, size_t);
-ssize_t vfs_listxattr(struct dentry *d, struct vfsmount *, char *list, size_t size);
+ssize_t vfs_getxattr(struct dentry *, struct vfsmount *, char *, void *,
+		     size_t, struct file *);
+ssize_t vfs_listxattr(struct dentry *d, struct vfsmount *, char *list,
+		      size_t size, struct file *);
 int vfs_setxattr(struct dentry *, struct vfsmount *, char *, void *, size_t,
-		 int);
-int vfs_removexattr(struct dentry *, struct vfsmount *, char *);
+		 int, struct file *);
+int vfs_removexattr(struct dentry *, struct vfsmount *, char *, struct file *);
 
 ssize_t generic_getxattr(struct dentry *dentry, const char *name, void *buffer, size_t size);
 ssize_t generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size);
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -380,7 +380,7 @@ int cap_bprm_secureexec (struct linux_bi
 }
 
 int cap_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, char *name,
-		       void *value, size_t size, int flags)
+		       void *value, size_t size, int flags, struct file *file)
 {
 	if (!strcmp(name, XATTR_NAME_CAPS)) {
 		if (!capable(CAP_SETFCAP))
@@ -394,7 +394,7 @@ int cap_inode_setxattr(struct dentry *de
 }
 
 int cap_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt,
-			  char *name)
+			  char *name, struct file *file)
 {
 	if (!strcmp(name, XATTR_NAME_CAPS)) {
 		if (!capable(CAP_SETFCAP))
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -377,7 +377,7 @@ static void dummy_inode_delete (struct i
 
 static int dummy_inode_setxattr (struct dentry *dentry, struct vfsmount *mnt,
 				 char *name, void *value, size_t size,
-				 int flags)
+				 int flags, struct file *file)
 {
 	if (!strncmp(name, XATTR_SECURITY_PREFIX,
 		     sizeof(XATTR_SECURITY_PREFIX) - 1) &&
@@ -394,18 +394,20 @@ static void dummy_inode_post_setxattr (s
 }
 
 static int dummy_inode_getxattr (struct dentry *dentry,
-			          struct vfsmount *mnt, char *name)
+			          struct vfsmount *mnt, char *name,
+				  struct file *file)
 {
 	return 0;
 }
 
-static int dummy_inode_listxattr (struct dentry *dentry, struct vfsmount *mnt)
+static int dummy_inode_listxattr (struct dentry *dentry, struct vfsmount *mnt,
+				  struct file *file)
 {
 	return 0;
 }
 
 static int dummy_inode_removexattr (struct dentry *dentry, struct vfsmount *mnt,
-				    char *name)
+				    char *name, struct file *file)
 {
 	if (!strncmp(name, XATTR_SECURITY_PREFIX,
 		     sizeof(XATTR_SECURITY_PREFIX) - 1) &&
--- a/security/security.c
+++ b/security/security.c
@@ -461,12 +461,13 @@ void security_inode_delete(struct inode 
 }
 
 int security_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt,
-			     char *name, void *value, size_t size, int flags)
+			    char *name, void *value, size_t size, int flags,
+			    struct file *file)
 {
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 		return 0;
 	return security_ops->inode_setxattr(dentry, mnt, name, value, size,
-					    flags);
+					    flags, file);
 }
 
 void security_inode_post_setxattr(struct dentry *dentry, struct vfsmount *mnt,
@@ -475,30 +476,32 @@ void security_inode_post_setxattr(struct
 {
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 		return;
-	security_ops->inode_post_setxattr(dentry, mnt, name, value, size, flags);
+	security_ops->inode_post_setxattr(dentry, mnt, name, value, size,
+					 flags);
 }
 
 int security_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt,
-			    char *name)
+			    char *name, struct file *file)
 {
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 		return 0;
-	return security_ops->inode_getxattr(dentry, mnt, name);
+	return security_ops->inode_getxattr(dentry, mnt, name, file);
 }
 
-int security_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt)
+int security_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt,
+			     struct file *file)
 {
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 		return 0;
-	return security_ops->inode_listxattr(dentry, mnt);
+	return security_ops->inode_listxattr(dentry, mnt, file);
 }
 
 int security_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt,
-			       char *name)
+			       char *name, struct file *file)
 {
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 		return 0;
-	return security_ops->inode_removexattr(dentry, mnt, name);
+	return security_ops->inode_removexattr(dentry, mnt, name, file);
 }
 
 int security_inode_need_killpriv(struct dentry *dentry)
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2561,7 +2561,7 @@ static int selinux_inode_setotherxattr(s
 
 static int selinux_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt,
 				  char *name, void *value, size_t size,
-				  int flags)
+				  int flags, struct file *file)
 {
 	struct task_security_struct *tsec = current->security;
 	struct inode *inode = dentry->d_inode;
@@ -2637,18 +2637,20 @@ static void selinux_inode_post_setxattr(
 }
 
 static int selinux_inode_getxattr (struct dentry *dentry, struct vfsmount *mnt,
-				   char *name)
+				   char *name, struct file *file)
 {
 	return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
 }
 
-static int selinux_inode_listxattr (struct dentry *dentry, struct vfsmount *mnt)
+static int selinux_inode_listxattr (struct dentry *dentry, struct vfsmount *mnt,
+				    struct file *file)
 {
 	return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
 }
 
 static int selinux_inode_removexattr (struct dentry *dentry,
-				      struct vfsmount *mnt, char *name)
+				      struct vfsmount *mnt, char *name,
+				      struct file *file)
 {
 	if (strcmp(name, XATTR_NAME_SELINUX))
 		return selinux_inode_setotherxattr(dentry, name);

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 35/47] Factor out sysctl pathname code
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (33 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 34/47] Pass struct file down the inode_*xattr security LSM hooks John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 36/47] Allow permission functions to tell between parent and leaf checks John, Johansen
                   ` (11 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Andreas Gruenbacher,
	John Johansen

[-- Attachment #1: sysctl-pathname.diff --]
[-- Type: text/plain, Size: 2871 bytes --]

Convert the selinux sysctl pathname computation code into a standalone
function.

Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
Reviewed-by: James Morris <jmorris@namei.org>

---
 include/linux/sysctl.h   |    2 ++
 kernel/sysctl.c          |   27 +++++++++++++++++++++++++++
 security/selinux/hooks.c |   35 +++++------------------------------
 3 files changed, 34 insertions(+), 30 deletions(-)

--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -977,6 +977,8 @@ extern int proc_doulongvec_minmax(struct
 extern int proc_doulongvec_ms_jiffies_minmax(struct ctl_table *table, int,
 				      struct file *, void __user *, size_t *, loff_t *);
 
+extern char *sysctl_pathname(ctl_table *, char *, int);
+
 extern int do_sysctl (int __user *name, int nlen,
 		      void __user *oldval, size_t __user *oldlenp,
 		      void __user *newval, size_t newlen);
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -1443,6 +1443,33 @@ void register_sysctl_root(struct ctl_tab
 	spin_unlock(&sysctl_lock);
 }
 
+char *sysctl_pathname(ctl_table *table, char *buffer, int buflen)
+{
+	if (buflen < 1)
+		return NULL;
+	buffer += --buflen;
+	*buffer = '\0';
+
+	while (table) {
+		int namelen = strlen(table->procname);
+
+		if (buflen < namelen + 1)
+			return NULL;
+		buflen -= namelen + 1;
+		buffer -= namelen;
+		memcpy(buffer, table->procname, namelen);
+		*--buffer = '/';
+		table = table->parent;
+	}
+	if (buflen < 4)
+		return NULL;
+	buffer -= 4;
+	memcpy(buffer, "/sys", 4);
+
+	return buffer;
+}
+EXPORT_SYMBOL(sysctl_pathname);
+
 #ifdef CONFIG_SYSCTL_SYSCALL
 int do_sysctl(int __user *name, int nlen, void __user *oldval, size_t __user *oldlenp,
 	       void __user *newval, size_t newlen)
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1659,40 +1659,15 @@ static int selinux_capable(struct task_s
 
 static int selinux_sysctl_get_sid(ctl_table *table, u16 tclass, u32 *sid)
 {
-	int buflen, rc;
-	char *buffer, *path, *end;
+	char *buffer, *path;
+	int rc = -ENOMEM;
 
-	rc = -ENOMEM;
 	buffer = (char*)__get_free_page(GFP_KERNEL);
 	if (!buffer)
 		goto out;
-
-	buflen = PAGE_SIZE;
-	end = buffer+buflen;
-	*--end = '\0';
-	buflen--;
-	path = end-1;
-	*path = '/';
-	while (table) {
-		const char *name = table->procname;
-		size_t namelen = strlen(name);
-		buflen -= namelen + 1;
-		if (buflen < 0)
-			goto out_free;
-		end -= namelen;
-		memcpy(end, name, namelen);
-		*--end = '/';
-		path = end;
-		table = table->parent;
-	}
-	buflen -= 4;
-	if (buflen < 0)
-		goto out_free;
-	end -= 4;
-	memcpy(end, "/sys", 4);
-	path = end;
-	rc = security_genfs_sid("proc", path, tclass, sid);
-out_free:
+	path = sysctl_pathname(table, buffer, PAGE_SIZE);
+	if (path)
+		rc = security_genfs_sid("proc", path, tclass, sid);
 	free_page((unsigned long)buffer);
 out:
 	return rc;

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 36/47] Allow permission functions to tell between parent and leaf checks
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (34 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 35/47] Factor out sysctl pathname code John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 37/47] Switch to vfs_permission() in do_path_lookup() John, Johansen
                   ` (10 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Andreas Gruenbacher,
	John Johansen

[-- Attachment #1: parent-permission.diff --]
[-- Type: text/plain, Size: 562 bytes --]

Set the LOOKUP_CONTINUE flag when checking parent permissions. This allows
permission functions to tell between parent and leaf checks.

Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/namei.c |    2 ++
 1 file changed, 2 insertions(+)

--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1511,6 +1511,8 @@ static inline int may_create(struct inod
 		return -EEXIST;
 	if (IS_DEADDIR(dir))
 		return -ENOENT;
+	if (nd)
+		nd->flags |= LOOKUP_CONTINUE;
 	return permission(dir,MAY_WRITE | MAY_EXEC, nd);
 }
 

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 37/47] Switch to vfs_permission() in do_path_lookup()
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (35 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 36/47] Allow permission functions to tell between parent and leaf checks John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 38/47] Switch to vfs_permission() in sys_fchdir() John, Johansen
                   ` (9 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Andreas Gruenbacher,
	John Johansen

[-- Attachment #1: do_path_lookup-nameidata.diff --]
[-- Type: text/plain, Size: 1024 bytes --]

Switch from file_permission() to vfs_permission() in do_path_lookup():
this avoids calling permission() with a NULL nameidata here.

Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/namei.c |    9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1150,24 +1150,21 @@ static int fastcall do_path_lookup(int d
 		path_get(&fs->pwd);
 		read_unlock(&fs->lock);
 	} else {
-		struct dentry *dentry;
-
 		file = fget_light(dfd, &fput_needed);
 		retval = -EBADF;
 		if (!file)
 			goto out_fail;
 
-		dentry = file->f_path.dentry;
+		nd->path = file->f_path;
 
 		retval = -ENOTDIR;
-		if (!S_ISDIR(dentry->d_inode->i_mode))
+		if (!S_ISDIR(nd->path.dentry->d_inode->i_mode))
 			goto fput_fail;
 
-		retval = file_permission(file, MAY_EXEC);
+		retval = vfs_permission(nd, MAY_EXEC);
 		if (retval)
 			goto fput_fail;
 
-		nd->path = file->f_path;
 		path_get(&file->f_path);
 
 		fput_light(file, fput_needed);

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 38/47] Switch to vfs_permission() in sys_fchdir()
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (36 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 37/47] Switch to vfs_permission() in do_path_lookup() John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 39/47] Fix file_permission() John, Johansen
                   ` (8 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Andreas Gruenbacher,
	John Johansen

[-- Attachment #1: sys_fchdir-nameidata.diff --]
[-- Type: text/plain, Size: 1033 bytes --]

Switch from file_permission() to vfs_permission() in sys_fchdir(): this
avoids calling permission() with a NULL nameidata here.

Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/open.c |   11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

--- a/fs/open.c
+++ b/fs/open.c
@@ -512,8 +512,8 @@ out:
 
 asmlinkage long sys_fchdir(unsigned int fd)
 {
+	struct nameidata nd;
 	struct file *file;
-	struct inode *inode;
 	int error;
 
 	error = -EBADF;
@@ -521,15 +521,16 @@ asmlinkage long sys_fchdir(unsigned int 
 	if (!file)
 		goto out;
 
-	inode = file->f_path.dentry->d_inode;
+	nd.path = file->f_path;
+	nd.flags = 0;
 
 	error = -ENOTDIR;
-	if (!S_ISDIR(inode->i_mode))
+	if (!S_ISDIR(nd.path.dentry->d_inode->i_mode))
 		goto out_putf;
 
-	error = file_permission(file, MAY_EXEC);
+	error = vfs_permission(&nd, MAY_EXEC);
 	if (!error)
-		set_fs_pwd(current->fs, &file->f_path);
+		set_fs_pwd(current->fs, &nd.path);
 out_putf:
 	fput(file);
 out:

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 39/47] Fix file_permission()
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (37 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 38/47] Switch to vfs_permission() in sys_fchdir() John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 40/47] Export audit subsystem for use by modules John, Johansen
                   ` (7 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Andreas Gruenbacher,
	John Johansen

[-- Attachment #1: file_permission-nameidata.diff --]
[-- Type: text/plain, Size: 759 bytes --]

We cannot easily switch from file_permission() to vfs_permission()
everywhere, so fix file_permission() to not use a NULL nameidata
for the remaining users.

Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 fs/namei.c |    8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

--- a/fs/namei.c
+++ b/fs/namei.c
@@ -313,7 +313,13 @@ int vfs_permission(struct nameidata *nd,
  */
 int file_permission(struct file *file, int mask)
 {
-	return permission(file->f_path.dentry->d_inode, mask, NULL);
+	struct nameidata nd;
+
+	nd.path.dentry = file->f_path.dentry;
+	nd.path.mnt = file->f_path.mnt;
+	nd.flags = LOOKUP_ACCESS;
+
+	return permission(nd.path.dentry->d_inode, mask, &nd);
 }
 
 /*

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 40/47] Export audit subsystem for use by modules
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (38 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 39/47] Fix file_permission() John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 41/47] AppArmor: Main Part John, Johansen
                   ` (6 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, Andreas Gruenbacher,
	John Johansen

[-- Attachment #1: apparmor-audit.diff --]
[-- Type: text/plain, Size: 2992 bytes --]

Update kenel audit range comments to show AppArmor's registered range of
1500-1599.  This range used to be reserved for LSPP but LSPP uses the
SE Linux range and the range was given to AppArmor.
Adds necessary export symbols for audit subsystem routines.
Changes audit_log_vformat to be externally visible (analagous to vprintf)
Patch is not in mainline -- pending AppArmor code submission to lkml

Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>

---
 include/linux/audit.h |   12 +++++++++++-
 kernel/audit.c        |    6 ++++--
 2 files changed, 15 insertions(+), 3 deletions(-)

--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -33,7 +33,7 @@
  * 1200 - 1299 messages internal to the audit daemon
  * 1300 - 1399 audit event messages
  * 1400 - 1499 SE Linux use
- * 1500 - 1599 kernel LSPP events
+ * 1500 - 1599 AppArmor use
  * 1600 - 1699 kernel crypto events
  * 1700 - 1799 kernel anomaly records
  * 1800 - 1999 future kernel use (maybe integrity labels and related events)
@@ -118,6 +118,13 @@
 #define AUDIT_MAC_UNLBL_STCADD	1416	/* NetLabel: add a static label */
 #define AUDIT_MAC_UNLBL_STCDEL	1417	/* NetLabel: del a static label */
 
+#define AUDIT_APPARMOR_AUDIT	1501	/* AppArmor audited grants */
+#define AUDIT_APPARMOR_ALLOWED	1502	/* Allowed Access for learning */
+#define AUDIT_APPARMOR_DENIED	1503
+#define AUDIT_APPARMOR_HINT	1504	/* Process Tracking information */
+#define AUDIT_APPARMOR_STATUS	1505	/* Changes in config */
+#define AUDIT_APPARMOR_ERROR	1506	/* Internal AppArmor Errors */
+
 #define AUDIT_FIRST_KERN_ANOM_MSG   1700
 #define AUDIT_LAST_KERN_ANOM_MSG    1799
 #define AUDIT_ANOM_PROMISCUOUS      1700 /* Device changed promiscuous mode */
@@ -515,6 +522,9 @@ extern void		    audit_log(struct audit_
 				      __attribute__((format(printf,4,5)));
 
 extern struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, int type);
+extern void		    audit_log_vformat(struct audit_buffer *ab,
+					      const char *fmt, va_list args)
+			    __attribute__((format(printf,2,0)));
 extern void		    audit_log_format(struct audit_buffer *ab,
 					     const char *fmt, ...)
 			    __attribute__((format(printf,2,3)));
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -1215,8 +1215,7 @@ static inline int audit_expand(struct au
  * will be called a second time.  Currently, we assume that a printk
  * can't format message larger than 1024 bytes, so we don't either.
  */
-static void audit_log_vformat(struct audit_buffer *ab, const char *fmt,
-			      va_list args)
+void audit_log_vformat(struct audit_buffer *ab, const char *fmt, va_list args)
 {
 	int len, avail;
 	struct sk_buff *skb;
@@ -1472,3 +1471,6 @@ EXPORT_SYMBOL(audit_log_start);
 EXPORT_SYMBOL(audit_log_end);
 EXPORT_SYMBOL(audit_log_format);
 EXPORT_SYMBOL(audit_log);
+EXPORT_SYMBOL_GPL(audit_log_vformat);
+EXPORT_SYMBOL_GPL(audit_log_untrustedstring);
+EXPORT_SYMBOL_GPL(audit_log_d_path);

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 41/47] AppArmor: Main Part
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (39 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 40/47] Export audit subsystem for use by modules John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 42/47] AppArmor: Module and LSM hooks John, Johansen
                   ` (5 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, John Johansen,
	Andreas Gruenbacher

[-- Attachment #1: apparmor-main.diff --]
[-- Type: text/plain, Size: 37083 bytes --]

The underlying functions by which the AppArmor LSM hooks are implemented.

Signed-off-by: John Johansen <jjohansen@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>

---
 security/apparmor/main.c | 1357 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 1357 insertions(+)

--- /dev/null
+++ b/security/apparmor/main.c
@@ -0,0 +1,1357 @@
+/*
+ *	Copyright (C) 2002-2007 Novell/SUSE
+ *
+ *	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, version 2 of the
+ *	License.
+ *
+ *	AppArmor Core
+ */
+
+#include <linux/security.h>
+#include <linux/namei.h>
+#include <linux/audit.h>
+#include <linux/mount.h>
+#include <linux/ptrace.h>
+
+#include "apparmor.h"
+
+#include "inline.h"
+
+/*
+ * Table of capability names: we generate it from capabilities.h.
+ */
+static const char *capability_names[] = {
+#include "capability_names.h"
+};
+
+struct aa_namespace *default_namespace;
+
+static int aa_inode_mode(struct inode *inode)
+{
+	/* if the inode doesn't exist the user is creating it */
+	if (!inode || current->fsuid == inode->i_uid)
+		return AA_USER_SHIFT;
+	return AA_OTHER_SHIFT;
+}
+
+/**
+ * aa_file_denied - check for @mask access on a file
+ * @profile: profile to check against
+ * @name: pathname of file
+ * @mask: permission mask requested for file
+ *
+ * Return %0 on success, or else the permissions in @mask that the
+ * profile denies.
+ */
+static int aa_file_denied(struct aa_profile *profile, const char *name,
+			  int mask)
+{
+	return (mask & ~aa_match(profile->file_rules, name));
+}
+
+/**
+ * aa_link_denied - check for permission to link a file
+ * @profile: profile to check against
+ * @link: pathname of link being created
+ * @target: pathname of target to be linked to
+ * @target_mode: UGO shift for target inode
+ * @request_mask: the permissions subset valid only if link succeeds
+ * Return %0 on success, or else the permissions that the profile denies.
+ */
+static int aa_link_denied(struct aa_profile *profile, const char *link,
+			  const char *target, int target_mode,
+			  int *request_mask)
+{
+	unsigned int state;
+	int l_mode, t_mode, denied_mask = 0;
+	int link_mask = AA_MAY_LINK << target_mode;
+
+	*request_mask = link_mask;
+
+	l_mode = aa_match_state(profile->file_rules, DFA_START, link, &state);
+	if (l_mode & link_mask) {
+		int mode;
+		/* test to see if target can be paired with link */
+		state = aa_dfa_null_transition(profile->file_rules, state);
+		mode = aa_match_state(profile->file_rules, state, target,
+				      NULL);
+
+		if (!(mode & link_mask))
+			denied_mask |= link_mask;
+		if (!(mode & (AA_LINK_SUBSET_TEST << target_mode)))
+			return denied_mask;
+	}
+
+	/* do link perm subset test */
+	t_mode = aa_match(profile->file_rules, target);
+
+	/* Ignore valid-profile-transition flags. */
+	l_mode &= ~AA_SHARED_PERMS;
+	t_mode &= ~AA_SHARED_PERMS;
+
+	*request_mask = l_mode | link_mask;
+
+	/* Link always requires 'l' on the link for both parts of the pair.
+	 * If a subset test is required a permission subset test of the
+	 * perms for the link are done against the user:group:other of the
+	 * target's 'r', 'w', 'x', 'a', 'z', and 'm' permissions.
+	 *
+	 * If the link has 'x', an exact match of all the execute flags
+	 * ('i', 'u', 'p').  safe exec is treated as a subset of unsafe exec
+ 	 */
+#define SUBSET_PERMS (AA_FILE_PERMS & ~AA_LINK_BITS)
+	denied_mask |= ~l_mode & link_mask;
+	if (l_mode & SUBSET_PERMS) {
+		denied_mask |= (l_mode & SUBSET_PERMS) & ~t_mode;
+		if (denied_mask & AA_EXEC_BITS)
+			denied_mask |= l_mode & AA_ALL_EXEC_MODS;
+		else if (l_mode & AA_EXEC_BITS) {
+			if (!(l_mode & AA_USER_EXEC_UNSAFE))
+				l_mode |= t_mode & AA_USER_EXEC_UNSAFE;
+			if (l_mode & AA_USER_EXEC &&
+			    (l_mode & AA_USER_EXEC_MODS) !=
+			    (t_mode & AA_USER_EXEC_MODS))
+				denied_mask |= AA_USER_EXEC |
+					(l_mode & AA_USER_EXEC_MODS);
+			if (!(l_mode & AA_OTHER_EXEC_UNSAFE))
+				l_mode |= t_mode & AA_OTHER_EXEC_UNSAFE;
+			if (l_mode & AA_OTHER_EXEC &&
+			    (l_mode & AA_OTHER_EXEC_MODS) !=
+			    (t_mode & AA_OTHER_EXEC_MODS))
+				denied_mask |= AA_OTHER_EXEC |
+					(l_mode & AA_OTHER_EXEC_MODS);
+		}
+	} else
+		denied_mask |= t_mode | link_mask;
+#undef SUBSET_PERMS
+
+	return denied_mask;
+}
+
+/**
+ * aa_get_name - compute the pathname of a file
+ * @dentry: dentry of the file
+ * @mnt: vfsmount of the file
+ * @buffer: buffer that aa_get_name() allocated
+ * @check: AA_CHECK_DIR is set if the file is a directory
+ *
+ * Returns a pointer to the beginning of the pathname (which usually differs
+ * from the beginning of the buffer), or an error code.
+ *
+ * We need @check to indicate whether the file is a directory or not because
+ * the file may not yet exist, and so we cannot check the inode's file type.
+ */
+static char *aa_get_name(struct dentry *dentry, struct vfsmount *mnt,
+			 char **buffer, int check)
+{
+	struct path path;
+	char *name;
+	int is_dir, size = 256;
+
+	is_dir = (check & AA_CHECK_DIR) ? 1 : 0;
+
+	path.dentry = dentry;
+	path.mnt = mnt;
+	for (;;) {
+		char *buf = kmalloc(size, GFP_KERNEL);
+		if (!buf)
+			return ERR_PTR(-ENOMEM);
+
+		name = d_namespace_path(&path, buf, size - is_dir);
+		if (!IS_ERR(name)) {
+			if (name[0] != '/') {
+				/*
+				 * This dentry is not connected to the
+				 * namespace root -- reject access.
+				 */
+				kfree(buf);
+				return ERR_PTR(-ENOENT);
+			}
+			if (is_dir && name[1] != '\0') {
+				/*
+				 * Append "/" to the pathname. The root
+				 * directory is a special case; it already
+				 * ends in slash.
+				 */
+				buf[size - 2] = '/';
+				buf[size - 1] = '\0';
+			}
+
+			*buffer = buf;
+			return name;
+		}
+		if (PTR_ERR(name) != -ENAMETOOLONG)
+			return name;
+
+		kfree(buf);
+		size <<= 1;
+		if (size > apparmor_path_max)
+			return ERR_PTR(-ENAMETOOLONG);
+	}
+}
+
+static inline void aa_put_name_buffer(char *buffer)
+{
+	kfree(buffer);
+}
+
+/**
+ * aa_perm_dentry - check if @profile allows @mask for a file
+ * @profile: profile to check against
+ * @dentry: dentry of the file
+ * @mnt: vfsmount o the file
+ * @sa: audit context
+ * @mask: requested profile permissions
+ * @check: kind of check to perform
+ *
+ * Returns 0 upon success, or else an error code.
+ *
+ * @check indicates the file type, and whether the file was accessed through
+ * an open file descriptor (AA_CHECK_FD) or not.
+ */
+static int aa_perm_dentry(struct aa_profile *profile, struct dentry *dentry,
+			  struct vfsmount *mnt, struct aa_audit *sa, int check)
+{
+	int error;
+	char *buffer = NULL;
+
+	sa->name = aa_get_name(dentry, mnt, &buffer, check);
+	sa->request_mask <<= aa_inode_mode(dentry->d_inode);
+	if (IS_ERR(sa->name)) {
+		/*
+		 * deleted files are given a pass on permission checks when
+		 * accessed through a file descriptor.
+		 */
+		if (PTR_ERR(sa->name) == -ENOENT && (check & AA_CHECK_FD))
+			sa->denied_mask = 0;
+		else {
+			sa->denied_mask = sa->request_mask;
+			sa->error_code = PTR_ERR(sa->name);
+		}
+		sa->name = NULL;
+	} else
+		sa->denied_mask = aa_file_denied(profile, sa->name,
+						 sa->request_mask);
+
+	if (!sa->denied_mask)
+		sa->error_code = 0;
+
+	error = aa_audit(profile, sa);
+	aa_put_name_buffer(buffer);
+
+	return error;
+}
+
+int alloc_default_namespace(void)
+{
+	struct aa_namespace *ns;
+	char *name = kstrdup("default", GFP_KERNEL);
+	if (!name)
+		return -ENOMEM;
+	ns = alloc_aa_namespace(name);
+	if (!ns) {
+		kfree(name);
+		return -ENOMEM;
+	}
+
+	write_lock(&profile_ns_list_lock);
+	default_namespace = ns;
+	aa_get_namespace(ns);
+	list_add(&ns->list, &profile_ns_list);
+	write_unlock(&profile_ns_list_lock);
+
+	return 0;
+}
+
+void free_default_namespace(void)
+{
+	write_lock(&profile_ns_list_lock);
+	list_del_init(&default_namespace->list);
+	write_unlock(&profile_ns_list_lock);
+	aa_put_namespace(default_namespace);
+	default_namespace = NULL;
+}
+
+static void aa_audit_file_sub_mask(struct audit_buffer *ab, char *buffer,
+				   int mask)
+{
+	char *m = buffer;
+
+	if (mask & AA_EXEC_MMAP)
+		*m++ = 'm';
+	if (mask & MAY_READ)
+		*m++ = 'r';
+	if (mask & MAY_WRITE)
+		*m++ = 'w';
+	else if (mask & MAY_APPEND)
+		*m++ = 'a';
+	if (mask & MAY_EXEC) {
+		if (mask & AA_EXEC_UNSAFE) {
+			switch(mask & AA_EXEC_MODIFIERS) {
+			case AA_EXEC_UNCONFINED:
+				*m++ = 'u';
+				break;
+			case AA_EXEC_PIX:
+				*m++ = 'p';
+				/* fall through */
+			case AA_EXEC_INHERIT:
+				*m++ = 'i';
+				break;
+			case AA_EXEC_PROFILE:
+				*m++ = 'p';
+				break;
+			}
+		} else {
+			switch(mask & AA_EXEC_MODIFIERS) {
+			case AA_EXEC_UNCONFINED:
+				*m++ = 'U';
+				break;
+			case AA_EXEC_PIX:
+				*m++ = 'P';
+				/* fall through */
+			case AA_EXEC_INHERIT:
+				*m++ = 'I';
+				break;
+			case AA_EXEC_PROFILE:
+				*m++ = 'P';
+				break;
+			}
+		}
+		*m++ = 'x';
+	}
+	if (mask & AA_MAY_LINK)
+		*m++ = 'l';
+	if (mask & AA_MAY_LOCK)
+		*m++ = 'k';
+	*m++ = '\0';
+}
+
+static void aa_audit_file_mask(struct audit_buffer *ab, const char *name,
+			       int mask)
+{
+	char user[10], other[10];
+
+	aa_audit_file_sub_mask(ab, user,
+			       (mask & AA_USER_PERMS) >> AA_USER_SHIFT);
+	aa_audit_file_sub_mask(ab, other,
+			       (mask & AA_OTHER_PERMS) >> AA_OTHER_SHIFT);
+
+	audit_log_format(ab, " %s=\"%s::%s\"", name, user, other);
+}
+
+/**
+ * aa_audit - Log an audit event to the audit subsystem
+ * @profile: profile to check against
+ * @sa: audit event
+ * @audit_cxt: audit context to log message to
+ * @type: audit event number
+ */
+static int aa_audit_base(struct aa_profile *profile, struct aa_audit *sa,
+			 struct audit_context *audit_cxt, int type)
+{
+	struct audit_buffer *ab = NULL;
+
+	ab = audit_log_start(audit_cxt, sa->gfp_mask, type);
+
+	if (!ab) {
+		AA_ERROR("Unable to log event (%d) to audit subsys\n",
+			 type);
+		 /* don't fail operations in complain mode even if logging
+		  * fails */
+		return type == AUDIT_APPARMOR_ALLOWED ? 0 : -ENOMEM;
+	}
+
+	if (sa->operation)
+		audit_log_format(ab, "operation=\"%s\"", sa->operation);
+
+	if (sa->info)
+		audit_log_format(ab, " info=\"%s\"", sa->info);
+
+	if (sa->request_mask)
+		aa_audit_file_mask(ab, "request_mask", sa->request_mask);
+
+	if (sa->denied_mask)
+		aa_audit_file_mask(ab, "denied_mask", sa->denied_mask);
+
+	if (sa->iattr) {
+		struct iattr *iattr = sa->iattr;
+
+		audit_log_format(ab, " attribute=\"%s%s%s%s%s%s%s\"",
+			iattr->ia_valid & ATTR_MODE ? "mode," : "",
+			iattr->ia_valid & ATTR_UID ? "uid," : "",
+			iattr->ia_valid & ATTR_GID ? "gid," : "",
+			iattr->ia_valid & ATTR_SIZE ? "size," : "",
+			iattr->ia_valid & (ATTR_ATIME | ATTR_ATIME_SET) ?
+				"atime," : "",
+			iattr->ia_valid & (ATTR_MTIME | ATTR_MTIME_SET) ?
+				"mtime," : "",
+			iattr->ia_valid & ATTR_CTIME ? "ctime," : "");
+	}
+
+	if (sa->task)
+		audit_log_format(ab, " task=%d", sa->task);
+
+	if (sa->parent)
+		audit_log_format(ab, " parent=%d", sa->parent);
+
+	if (sa->name) {
+		audit_log_format(ab, " name=");
+		audit_log_untrustedstring(ab, sa->name);
+	}
+
+	if (sa->name2) {
+		audit_log_format(ab, " name2=");
+		audit_log_untrustedstring(ab, sa->name2);
+	}
+
+	audit_log_format(ab, " pid=%d", current->pid);
+
+	if (profile) {
+		audit_log_format(ab, " profile=");
+		audit_log_untrustedstring(ab, profile->name);
+
+		audit_log_format(ab, " namespace=");
+		audit_log_untrustedstring(ab, profile->ns->name);
+	}
+
+	audit_log_end(ab);
+
+	return type == AUDIT_APPARMOR_ALLOWED ? 0 : sa->error_code;
+}
+
+/**
+ * aa_audit_syscallreject - Log a syscall rejection to the audit subsystem
+ * @profile: profile to check against
+ * @gfp: memory allocation flags
+ * @msg: string describing syscall being rejected
+ */
+int aa_audit_syscallreject(struct aa_profile *profile, gfp_t gfp,
+			   const char *msg)
+{
+	struct aa_audit sa;
+	memset(&sa, 0, sizeof(sa));
+	sa.operation = "syscall";
+	sa.name = msg;
+	sa.gfp_mask = gfp;
+	sa.error_code = -EPERM;
+
+	return aa_audit_base(profile, &sa, current->audit_context,
+			     AUDIT_APPARMOR_DENIED);
+}
+
+int aa_audit_message(struct aa_profile *profile, struct aa_audit *sa,
+		      int type)
+{
+	struct audit_context *audit_cxt;
+
+	audit_cxt = apparmor_logsyscall ? current->audit_context : NULL;
+	return aa_audit_base(profile, sa, audit_cxt, type);
+}
+
+void aa_audit_hint(struct aa_profile *profile, struct aa_audit *sa)
+{
+	aa_audit_message(profile, sa, AUDIT_APPARMOR_HINT);
+}
+
+void aa_audit_status(struct aa_profile *profile, struct aa_audit *sa)
+{
+	aa_audit_message(profile, sa, AUDIT_APPARMOR_STATUS);
+}
+
+int aa_audit_reject(struct aa_profile *profile, struct aa_audit *sa)
+{
+	return aa_audit_message(profile, sa, AUDIT_APPARMOR_DENIED);
+}
+
+/**
+ * aa_audit - Log an audit event to the audit subsystem
+ * @profile: profile to check against
+ * @sa: audit event
+ */
+int aa_audit(struct aa_profile *profile, struct aa_audit *sa)
+{
+	int type = AUDIT_APPARMOR_DENIED;
+	struct audit_context *audit_cxt;
+
+	if (likely(!sa->error_code)) {
+		if (likely(!PROFILE_AUDIT(profile)))
+			/* nothing to log */
+			return 0;
+		else
+			type = AUDIT_APPARMOR_AUDIT;
+	} else if (PROFILE_COMPLAIN(profile)) {
+		type = AUDIT_APPARMOR_ALLOWED;
+	}
+
+	audit_cxt = apparmor_logsyscall ? current->audit_context : NULL;
+	return aa_audit_base(profile, sa, audit_cxt, type);
+}
+
+/**
+ * aa_attr - check if attribute change is allowed
+ * @profile: profile to check against
+ * @dentry: dentry of the file to check
+ * @mnt: vfsmount of the file to check
+ * @iattr: attribute changes requested
+ */
+int aa_attr(struct aa_profile *profile, struct dentry *dentry,
+	    struct vfsmount *mnt, struct iattr *iattr)
+{
+	struct inode *inode = dentry->d_inode;
+	int error, check;
+	struct aa_audit sa;
+
+	memset(&sa, 0, sizeof(sa));
+	sa.operation = "setattr";
+	sa.gfp_mask = GFP_KERNEL;
+	sa.iattr = iattr;
+	sa.request_mask = MAY_WRITE;
+	sa.error_code = -EACCES;
+
+	check = 0;
+	if (inode && S_ISDIR(inode->i_mode))
+		check |= AA_CHECK_DIR;
+	if (iattr->ia_valid & ATTR_FILE)
+		check |= AA_CHECK_FD;
+
+	error = aa_perm_dentry(profile, dentry, mnt, &sa, check);
+
+	return error;
+}
+
+/**
+ * aa_perm_xattr - check if xattr attribute change is allowed
+ * @profile: profile to check against
+ * @dentry: dentry of the file to check
+ * @mnt: vfsmount of the file to check
+ * @operation: xattr operation being done
+ * @mask: access mode requested
+ * @check: kind of check to perform
+ */
+int aa_perm_xattr(struct aa_profile *profile, const char *operation,
+		  struct dentry *dentry, struct vfsmount *mnt, int mask,
+		  int check)
+{
+	struct inode *inode = dentry->d_inode;
+	int error;
+	struct aa_audit sa;
+
+	memset(&sa, 0, sizeof(sa));
+	sa.operation = operation;
+	sa.gfp_mask = GFP_KERNEL;
+	sa.request_mask = mask;
+	sa.error_code = -EACCES;
+
+	if (inode && S_ISDIR(inode->i_mode))
+		check |= AA_CHECK_DIR;
+
+	error = aa_perm_dentry(profile, dentry, mnt, &sa, check);
+
+	return error;
+}
+
+/**
+ * aa_perm - basic apparmor permissions check
+ * @profile: profile to check against
+ * @dentry: dentry of the file to check
+ * @mnt: vfsmount of the file to check
+ * @mask: access mode requested
+ * @check: kind of check to perform
+ *
+ * Determine if access @mask for the file is authorized by @profile.
+ * Returns 0 on success, or else an error code.
+ */
+int aa_perm(struct aa_profile *profile, const char *operation,
+	    struct dentry *dentry, struct vfsmount *mnt, int mask, int check)
+{
+	struct aa_audit sa;
+	int error = 0;
+
+	if (mask == 0)
+		goto out;
+
+	memset(&sa, 0, sizeof(sa));
+	sa.operation = operation;
+	sa.gfp_mask = GFP_KERNEL;
+	sa.request_mask = mask;
+	sa.error_code = -EACCES;
+
+	error = aa_perm_dentry(profile, dentry, mnt, &sa, check);
+
+out:
+	return error;
+}
+
+/**
+ * aa_perm_dir
+ * @profile: profile to check against
+ * @dentry: dentry of directory to check
+ * @mnt: vfsmount of directory to check
+ * @operation: directory operation being performed
+ * @mask: access mode requested
+ *
+ * Determine if directory operation (make/remove) for dentry is authorized
+ * by @profile.
+ * Returns 0 on success, or else an error code.
+ */
+int aa_perm_dir(struct aa_profile *profile, const char *operation,
+		struct dentry *dentry, struct vfsmount *mnt, int mask)
+{
+	struct aa_audit sa;
+
+	memset(&sa, 0, sizeof(sa));
+	sa.operation = operation;
+	sa.gfp_mask = GFP_KERNEL;
+	sa.request_mask = mask;
+	sa.error_code = -EACCES;
+
+	return aa_perm_dentry(profile, dentry, mnt, &sa, AA_CHECK_DIR);
+}
+
+int aa_perm_path(struct aa_profile *profile, const char *operation,
+		 const char *name, int mask, uid_t uid)
+{
+	struct aa_audit sa;
+
+	memset(&sa, 0, sizeof(sa));
+	sa.operation = operation;
+	sa.gfp_mask = GFP_KERNEL;
+	sa.request_mask = mask;
+	sa.name = name;
+	if (current->fsuid == uid)
+		sa.request_mask = mask << AA_USER_SHIFT;
+	else
+		sa.request_mask = mask << AA_OTHER_SHIFT;
+
+	sa.denied_mask = aa_file_denied(profile, name, sa.request_mask) ;
+	sa.error_code = sa.denied_mask ? -EACCES : 0;
+
+	return aa_audit(profile, &sa);
+}
+
+/**
+ * aa_capability - test permission to use capability
+ * @cxt: aa_task_context with profile to check against
+ * @cap: capability to be tested
+ *
+ * Look up capability in profile capability set.
+ * Returns 0 on success, or else an error code.
+ */
+int aa_capability(struct aa_task_context *cxt, int cap)
+{
+	int error = cap_raised(cxt->profile->capabilities, cap) ? 0 : -EPERM;
+	struct aa_audit sa;
+
+	/* test if cap has alread been logged */
+	if (cap_raised(cxt->caps_logged, cap)) {
+		if (PROFILE_COMPLAIN(cxt->profile))
+			error = 0;
+		return error;
+	} else
+		/* don't worry about rcu replacement of the cxt here.
+		 * caps_logged is a cache to reduce the occurence of
+		 * duplicate messages in the log.  The worst that can
+		 * happen is duplicate capability messages shows up in
+		 * the audit log
+		 */
+		cap_raise(cxt->caps_logged, cap);
+
+	memset(&sa, 0, sizeof(sa));
+	sa.operation = "capable";
+	sa.gfp_mask = GFP_ATOMIC;
+	sa.name = capability_names[cap];
+	sa.error_code = error;
+
+	error = aa_audit(cxt->profile, &sa);
+
+	return error;
+}
+
+/* must be used inside rcu_read_lock or task_lock */
+int aa_may_ptrace(struct aa_task_context *cxt, struct aa_profile *tracee)
+{
+	if (!cxt || cxt->profile == tracee)
+		return 0;
+	return aa_capability(cxt, CAP_SYS_PTRACE);
+}
+
+/**
+ * aa_link - hard link check
+ * @profile: profile to check against
+ * @link: dentry of link being created
+ * @link_mnt: vfsmount of link being created
+ * @target: dentry of link target
+ * @target_mnt: vfsmunt of link target
+ *
+ * Returns 0 on success, or else an error code.
+ */
+int aa_link(struct aa_profile *profile,
+	    struct dentry *link, struct vfsmount *link_mnt,
+	    struct dentry *target, struct vfsmount *target_mnt)
+{
+	int error;
+	struct aa_audit sa;
+	char *buffer = NULL, *buffer2 = NULL;
+
+	memset(&sa, 0, sizeof(sa));
+	sa.operation = "inode_link";
+	sa.gfp_mask = GFP_KERNEL;
+	sa.name = aa_get_name(link, link_mnt, &buffer, 0);
+	sa.name2 = aa_get_name(target, target_mnt, &buffer2, 0);
+
+	if (IS_ERR(sa.name)) {
+		sa.error_code = PTR_ERR(sa.name);
+		sa.name = NULL;
+	}
+	if (IS_ERR(sa.name2)) {
+		sa.error_code = PTR_ERR(sa.name2);
+		sa.name2 = NULL;
+	}
+
+	if (sa.name && sa.name2) {
+		sa.denied_mask = aa_link_denied(profile, sa.name, sa.name2,
+						aa_inode_mode(target->d_inode),
+						&sa.request_mask);
+		sa.error_code = sa.denied_mask ? -EACCES : 0;
+	}
+
+	error = aa_audit(profile, &sa);
+
+	aa_put_name_buffer(buffer);
+	aa_put_name_buffer(buffer2);
+
+	return error;
+}
+
+/*******************************
+ * Global task related functions
+ *******************************/
+
+/**
+ * aa_clone - initialize the task context for a new task
+ * @child: task that is being created
+ *
+ * Returns 0 on success, or else an error code.
+ */
+int aa_clone(struct task_struct *child)
+{
+	struct aa_task_context *cxt, *child_cxt;
+	struct aa_profile *profile;
+
+	if (!aa_task_context(current))
+		return 0;
+	child_cxt = aa_alloc_task_context(GFP_KERNEL);
+	if (!child_cxt)
+		return -ENOMEM;
+
+repeat:
+	profile = aa_get_profile(current);
+	if (profile) {
+		lock_profile(profile);
+		cxt = aa_task_context(current);
+		if (unlikely(profile->isstale || !cxt ||
+			     cxt->profile != profile)) {
+			/**
+			 * Race with profile replacement or removal, or with
+			 * task context removal.
+			 */
+			unlock_profile(profile);
+			aa_put_profile(profile);
+			goto repeat;
+		}
+
+		/* No need to grab the child's task lock here. */
+		aa_change_task_context(child, child_cxt, profile,
+				       cxt->cookie, cxt->previous_profile);
+		unlock_profile(profile);
+
+		if (APPARMOR_COMPLAIN(child_cxt) &&
+		    profile == profile->ns->null_complain_profile) {
+			struct aa_audit sa;
+			memset(&sa, 0, sizeof(sa));
+			sa.operation = "clone";
+			sa.gfp_mask = GFP_KERNEL;
+			sa.task = child->pid;
+			aa_audit_hint(profile, &sa);
+		}
+		aa_put_profile(profile);
+	} else
+		aa_free_task_context(child_cxt);
+
+	return 0;
+}
+
+static struct aa_profile *
+aa_register_find(struct aa_profile *profile, const char *name, int mandatory,
+		 int complain, struct aa_audit *sa)
+{
+	struct aa_profile *new_profile;
+
+	/* Locate new profile */
+	if (profile)
+		new_profile = aa_find_profile(profile->ns, name);
+	else
+		new_profile = aa_find_profile(default_namespace, name);
+
+	if (new_profile) {
+		AA_DEBUG("%s: setting profile %s\n",
+			 __FUNCTION__, new_profile->name);
+	} else if (mandatory && profile) {
+		sa->info = "mandatory profile missing";
+		sa->denied_mask = sa->request_mask;	/* shifted MAY_EXEC */
+		if (complain) {
+			aa_audit_hint(profile, sa);
+			new_profile =
+			    aa_dup_profile(profile->ns->null_complain_profile);
+		} else {
+			aa_audit_reject(profile, sa);
+			return ERR_PTR(-EACCES);  /* was -EPERM */
+		}
+	} else {
+		/* Only way we can get into this code is if task
+		 * is unconfined, or pix.
+		 */
+		AA_DEBUG("%s: No profile found for exec image '%s'\n",
+			 __FUNCTION__,
+			 name);
+	}
+	return new_profile;
+}
+
+/**
+ * aa_register - register a new program
+ * @bprm: binprm of program being registered
+ *
+ * Try to register a new program during execve().  This should give the
+ * new program a valid aa_task_context if confined.
+ */
+int aa_register(struct linux_binprm *bprm)
+{
+	const char *filename;
+	char  *buffer = NULL;
+	struct file *filp = bprm->file;
+	struct aa_profile *profile, *old_profile, *new_profile = NULL;
+	int exec_mode, complain = 0, shift;
+	struct aa_audit sa;
+
+	AA_DEBUG("%s\n", __FUNCTION__);
+
+	filename = aa_get_name(filp->f_dentry, filp->f_vfsmnt, &buffer, 0);
+	if (IS_ERR(filename)) {
+		AA_ERROR("%s: Failed to get filename", __FUNCTION__);
+		return -ENOENT;
+	}
+
+	shift = aa_inode_mode(filp->f_dentry->d_inode);
+	exec_mode = AA_EXEC_UNSAFE << shift;
+
+	memset(&sa, 0, sizeof(sa));
+	sa.operation = "exec";
+	sa.gfp_mask = GFP_KERNEL;
+	sa.name = filename;
+	sa.request_mask = MAY_EXEC << shift;
+
+repeat:
+	profile = aa_get_profile(current);
+	if (profile) {
+		complain = PROFILE_COMPLAIN(profile);
+
+		/* Confined task, determine what mode inherit, unconfined or
+		 * mandatory to load new profile
+		 */
+		exec_mode = aa_match(profile->file_rules, filename);
+
+		if (exec_mode & sa.request_mask) {
+			switch ((exec_mode >> shift) & AA_EXEC_MODIFIERS) {
+			case AA_EXEC_INHERIT:
+				AA_DEBUG("%s: INHERIT %s\n",
+					 __FUNCTION__,
+					 filename);
+				/* nothing to be done here */
+				goto cleanup;
+
+			case AA_EXEC_UNCONFINED:
+				AA_DEBUG("%s: UNCONFINED %s\n",
+					 __FUNCTION__,
+					 filename);
+
+				/* detach current profile */
+				new_profile = NULL;
+				break;
+
+			case AA_EXEC_PROFILE:
+				AA_DEBUG("%s: PROFILE %s\n",
+					 __FUNCTION__,
+					 filename);
+				new_profile = aa_register_find(profile,
+							       filename,
+							       1, complain,
+							       &sa);
+				break;
+			case AA_EXEC_PIX:
+				AA_DEBUG("%s: PROFILE %s\n",
+					 __FUNCTION__,
+					 filename);
+				new_profile = aa_register_find(profile,
+							       filename,
+							       0, complain,
+							       &sa);
+				if (!new_profile)
+					/* inherit - nothing to be done here */
+					goto cleanup;
+				break;
+			}
+
+		} else if (complain) {
+			/* There was no entry in calling profile
+			 * describing mode to execute image in.
+			 * Drop into null-profile (disabling secure exec).
+			 */
+			new_profile =
+			    aa_dup_profile(profile->ns->null_complain_profile);
+			exec_mode |= AA_EXEC_UNSAFE << shift;
+		} else {
+			sa.denied_mask = sa.request_mask;
+			aa_audit_reject(profile, &sa);
+			new_profile = ERR_PTR(-EPERM);
+		}
+	} else {
+		/* Unconfined task, load profile if it exists */
+		new_profile = aa_register_find(NULL, filename, 0, 0, &sa);
+		if (new_profile == NULL)
+			goto cleanup;
+	}
+
+	if (IS_ERR(new_profile))
+		goto cleanup;
+
+	old_profile = __aa_replace_profile(current, new_profile);
+	if (IS_ERR(old_profile)) {
+		aa_put_profile(new_profile);
+		aa_put_profile(profile);
+		if (PTR_ERR(old_profile) == -ESTALE)
+			goto repeat;
+		if (PTR_ERR(old_profile) == -EPERM) {
+			sa.denied_mask = sa.request_mask;
+			sa.info = "unable to set profile due to ptrace";
+			sa.task = current->parent->pid;
+			aa_audit_reject(profile, &sa);
+		}
+		new_profile = old_profile;
+		goto cleanup;
+	}
+	aa_put_profile(old_profile);
+	aa_put_profile(profile);
+
+	/* Handle confined exec.
+	 * Can be at this point for the following reasons:
+	 * 1. unconfined switching to confined
+	 * 2. confined switching to different confinement
+	 * 3. confined switching to unconfined
+	 *
+	 * Cases 2 and 3 are marked as requiring secure exec
+	 * (unless policy specified "unsafe exec")
+	 */
+	if (!(exec_mode & (AA_EXEC_UNSAFE << shift))) {
+		unsigned long bprm_flags;
+
+		bprm_flags = AA_SECURE_EXEC_NEEDED;
+		bprm->security = (void*)
+			((unsigned long)bprm->security | bprm_flags);
+	}
+
+	if (complain && new_profile &&
+	    new_profile == new_profile->ns->null_complain_profile) {
+		sa.request_mask = 0;
+		sa.name = NULL;
+		sa.info = "set profile";
+		aa_audit_hint(new_profile, &sa);
+	}
+cleanup:
+	aa_put_name_buffer(buffer);
+	if (IS_ERR(new_profile))
+		return PTR_ERR(new_profile);
+	aa_put_profile(new_profile);
+	return 0;
+}
+
+/**
+ * aa_release - release a task context
+ * @task: task being released
+ *
+ * This is called after a task has exited and the parent has reaped it.
+ */
+void aa_release(struct task_struct *task)
+{
+	struct aa_task_context *cxt;
+	struct aa_profile *profile;
+	/*
+	 * While the task context is still on a profile's task context
+	 * list, another process could replace the profile under us,
+	 * leaving us with a locked profile that is no longer attached
+	 * to this task. So after locking the profile, we check that
+	 * the profile is still attached.  The profile lock is
+	 * sufficient to prevent the replacement race so we do not lock
+	 * the task.
+	 *
+	 * Use lock subtyping to avoid lockdep reporting a false irq
+	 * possible inversion between the task_lock and profile_lock
+	 *
+	 * We also avoid taking the task_lock here because lock_dep
+	 * would report another false {softirq-on-W} potential irq_lock
+	 * inversion.
+	 *
+	 * If the task does not have a profile attached we are safe;
+	 * nothing can race with us at this point.
+	 */
+
+repeat:
+	profile = aa_get_profile(task);
+	if (profile) {
+		lock_profile_nested(profile, aa_lock_task_release);
+		cxt = aa_task_context(task);
+		if (unlikely(!cxt || cxt->profile != profile)) {
+			unlock_profile(profile);
+			aa_put_profile(profile);
+			goto repeat;
+		}
+		aa_change_task_context(task, NULL, NULL, 0, NULL);
+		unlock_profile(profile);
+		aa_put_profile(profile);
+	}
+}
+
+static int do_change_profile(struct aa_profile *expected,
+			     struct aa_namespace *ns, const char *name,
+			     u64 cookie, int restore, struct aa_audit *sa)
+{
+	struct aa_profile *new_profile = NULL, *old_profile = NULL,
+		*previous_profile = NULL;
+	struct aa_task_context *new_cxt, *cxt;
+	int error = 0;
+
+	sa->name = name;
+
+	new_cxt = aa_alloc_task_context(GFP_KERNEL);
+	if (!new_cxt)
+		return -ENOMEM;
+
+	new_profile = aa_find_profile(ns, name);
+	if (!new_profile && !restore) {
+		if (!PROFILE_COMPLAIN(expected))
+			return -ENOENT;
+		new_profile = aa_dup_profile(ns->null_complain_profile);
+	}
+
+	cxt = lock_task_and_profiles(current, new_profile);
+	if (!cxt) {
+		error = -EPERM;
+		goto out;
+	}
+	old_profile = cxt->profile;
+
+	if (cxt->profile != expected || (new_profile && new_profile->isstale)) {
+		error = -ESTALE;
+		goto out;
+	}
+
+	if (cxt->previous_profile) {
+		if (cxt->cookie != cookie) {
+			error = -EACCES;
+			sa->info = "killing process";
+			aa_audit_reject(cxt->profile, sa);
+			/* terminate process */
+			(void)send_sig_info(SIGKILL, NULL, current);
+			goto out;
+		}
+
+		if (!restore)
+			previous_profile = cxt->previous_profile;
+	} else
+		previous_profile = cxt->profile;
+
+	if ((current->ptrace & PT_PTRACED) && aa_may_ptrace(cxt, new_profile)) {
+		error = -EACCES;
+		goto out;
+	}
+
+	if (new_profile == ns->null_complain_profile)
+		aa_audit_hint(cxt->profile, sa);
+
+	if (APPARMOR_AUDIT(cxt))
+		aa_audit_message(cxt->profile, sa, AUDIT_APPARMOR_AUDIT);
+
+	if (!restore && cookie)
+		aa_change_task_context(current, new_cxt, new_profile, cookie,
+				       previous_profile);
+	else
+		/* either return to previous_profile, or a permanent change */
+		aa_change_task_context(current, new_cxt, new_profile, 0, NULL);
+
+out:
+	if (aa_task_context(current) != new_cxt)
+		aa_free_task_context(new_cxt);
+	task_unlock(current);
+	unlock_both_profiles(old_profile, new_profile);
+	aa_put_profile(new_profile);
+	return error;
+}
+
+/**
+ * aa_change_profile - perform a one-way profile transition
+ * @ns_name: name of the profile namespace to change to
+ * @name: name of profile to change to
+ * Change to new profile @name.  Unlike with hats, there is no way
+ * to change back.
+ *
+ * Returns %0 on success, error otherwise.
+ */
+int aa_change_profile(const char *ns_name, const char *name)
+{
+	struct aa_task_context *cxt;
+	struct aa_profile *profile;
+	struct aa_namespace *ns = NULL;
+	struct aa_audit sa;
+	unsigned int state;
+	int error = -EINVAL;
+
+	if (!name)
+		return -EINVAL;
+
+	memset(&sa, 0, sizeof(sa));
+	sa.gfp_mask = GFP_ATOMIC;
+	sa.operation = "change_profile";
+
+repeat:
+	task_lock(current);
+	cxt = aa_task_context(current);
+	if (!cxt) {
+		task_unlock(current);
+		return -EPERM;
+	}
+	profile = aa_dup_profile(cxt->profile);
+	task_unlock(current);
+
+	if (ns_name)
+		ns = aa_find_namespace(ns_name);
+	else
+		ns = aa_get_namespace(profile->ns);
+	if (!ns) {
+		aa_put_profile(profile);
+		return -ENOENT;
+	}
+
+	if (PROFILE_COMPLAIN(profile) ||
+	    (ns == profile->ns &&
+	     (aa_match(profile->file_rules, name) & AA_CHANGE_PROFILE)))
+		error = do_change_profile(profile, ns, name, 0, 0, &sa);
+	else {
+		/* check for a rule with a namespace prepended */
+		aa_match_state(profile->file_rules, DFA_START, ns->name,
+			       &state);
+		state = aa_dfa_null_transition(profile->file_rules, state);
+		if ((aa_match_state(profile->file_rules, state, name, NULL) &
+		      AA_CHANGE_PROFILE))
+			error = do_change_profile(profile, ns, name, 0, 0,
+						  &sa);
+		else
+			/* no permission to transition to profile @name */
+			error = -EACCES;
+	}
+
+	aa_put_namespace(ns);
+	aa_put_profile(profile);
+	if (error == -ESTALE)
+		goto repeat;
+
+	return error;
+}
+
+/**
+ * aa_change_hat - change hat to/from subprofile
+ * @hat_name: hat to change to
+ * @cookie: magic value to validate the hat change
+ *
+ * Change to new @hat_name, and store the @hat_magic in the current task
+ * context.  If the new @hat_name is %NULL and the @cookie matches that
+ * stored in the current task context and is not 0, return to the top level
+ * profile.
+ * Returns %0 on success, error otherwise.
+ */
+int aa_change_hat(const char *hat_name, u64 cookie)
+{
+	struct aa_task_context *cxt;
+	struct aa_profile *profile, *previous_profile;
+	struct aa_audit sa;
+	int error = 0;
+
+	memset(&sa, 0, sizeof(sa));
+	sa.gfp_mask = GFP_ATOMIC;
+	sa.operation = "change_hat";
+
+repeat:
+	task_lock(current);
+	cxt = aa_task_context(current);
+	if (!cxt) {
+		task_unlock(current);
+		return -EPERM;
+	}
+	profile = aa_dup_profile(cxt->profile);
+	previous_profile = aa_dup_profile(cxt->previous_profile);
+	task_unlock(current);
+
+	if (hat_name) {
+		char *name, *profile_name;
+		if (previous_profile)
+			profile_name = previous_profile->name;
+		else
+			profile_name = profile->name;
+
+		name = kmalloc(strlen(hat_name) + 3 + strlen(profile_name),
+			       GFP_KERNEL);
+		if (!name) {
+			error = -ENOMEM;
+			goto out;
+		}
+		sprintf(name, "%s//%s", profile_name, hat_name);
+		error = do_change_profile(profile, profile->ns, name, cookie,
+					  0, &sa);
+		kfree(name);
+	} else if (previous_profile)
+		error = do_change_profile(profile, profile->ns,
+					  previous_profile->name, cookie, 1,
+					  &sa);
+	/* else ignore restores when there is no saved profile */
+
+out:
+	aa_put_profile(previous_profile);
+	aa_put_profile(profile);
+	if (error == -ESTALE)
+		goto repeat;
+
+	return error;
+}
+
+/**
+ * __aa_replace_profile - replace a task's profile
+ * @task: task to switch the profile of
+ * @profile: profile to switch to
+ *
+ * Returns a handle to the previous profile upon success, or else an
+ * error code.
+ */
+struct aa_profile *__aa_replace_profile(struct task_struct *task,
+					struct aa_profile *profile)
+{
+	struct aa_task_context *cxt, *new_cxt = NULL;
+	struct aa_profile *old_profile = NULL;
+
+	if (profile) {
+		new_cxt = aa_alloc_task_context(GFP_KERNEL);
+		if (!new_cxt)
+			return ERR_PTR(-ENOMEM);
+	}
+
+	cxt = lock_task_and_profiles(task, profile);
+	if (unlikely(profile && profile->isstale)) {
+		task_unlock(task);
+		unlock_both_profiles(profile, cxt ? cxt->profile : NULL);
+		aa_free_task_context(new_cxt);
+		return ERR_PTR(-ESTALE);
+	}
+
+	if ((current->ptrace & PT_PTRACED) && aa_may_ptrace(cxt, profile)) {
+		task_unlock(task);
+		unlock_both_profiles(profile, cxt ? cxt->profile : NULL);
+		aa_free_task_context(new_cxt);
+		return ERR_PTR(-EPERM);
+	}
+
+	if (cxt)
+		old_profile = aa_dup_profile(cxt->profile);
+	aa_change_task_context(task, new_cxt, profile, 0, NULL);
+
+	task_unlock(task);
+	unlock_both_profiles(profile, old_profile);
+	return old_profile;
+}
+
+/**
+ * lock_task_and_profiles - lock the task and confining profiles and @profile
+ * @task: task to lock
+ * @profile: extra profile to lock in addition to the current profile
+ *
+ * Handle the spinning on locking to make sure the task context and
+ * profile are consistent once all locks are aquired.
+ *
+ * return the aa_task_context currently confining the task.  The task lock
+ * will be held whether or not the task is confined.
+ */
+struct aa_task_context *
+lock_task_and_profiles(struct task_struct *task, struct aa_profile *profile)
+{
+	struct aa_task_context *cxt;
+	struct aa_profile *old_profile = NULL;
+
+	rcu_read_lock();
+repeat:
+	cxt = aa_task_context(task);
+	if (cxt)
+		old_profile = cxt->profile;
+
+	lock_both_profiles(profile, old_profile);
+	task_lock(task);
+
+	/* check for race with profile transition, replacement or removal */
+	if (unlikely(cxt != aa_task_context(task))) {
+		task_unlock(task);
+		unlock_both_profiles(profile, old_profile);
+		old_profile = NULL;
+		goto repeat;
+	}
+	rcu_read_unlock();
+	return cxt;
+}
+
+static void free_aa_task_context_rcu_callback(struct rcu_head *head)
+{
+	struct aa_task_context *cxt;
+
+	cxt = container_of(head, struct aa_task_context, rcu);
+	aa_free_task_context(cxt);
+}
+
+/**
+ * aa_change_task_context - switch a task to use a new context and profile
+ * @task: task that is having its task context changed
+ * @new_cxt: new task context to use after the switch
+ * @profile: new profile to use after the switch
+ * @cookie: magic value to switch to
+ * @previous_profile: profile the task can return to
+ */
+void aa_change_task_context(struct task_struct *task,
+			    struct aa_task_context *new_cxt,
+			    struct aa_profile *profile, u64 cookie,
+			    struct aa_profile *previous_profile)
+{
+	struct aa_task_context *old_cxt = aa_task_context(task);
+
+	if (old_cxt) {
+		list_del_init(&old_cxt->list);
+		call_rcu(&old_cxt->rcu, free_aa_task_context_rcu_callback);
+	}
+
+	if (new_cxt) {
+		/* clear the caps_logged cache, so that new profile/hat has
+		 * chance to emit its own set of cap messages */
+		new_cxt->caps_logged = __cap_empty_set;
+		new_cxt->cookie = cookie;
+		new_cxt->task = task;
+		new_cxt->profile = aa_dup_profile(profile);
+		new_cxt->previous_profile = aa_dup_profile(previous_profile);
+		list_move(&new_cxt->list, &profile->task_contexts);
+	}
+	rcu_assign_pointer(task->security, new_cxt);
+}

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 42/47] AppArmor: Module and LSM hooks
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (40 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 41/47] AppArmor: Main Part John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 43/47] AppArmor: Profile loading and manipulation, pathname matching John, Johansen
                   ` (4 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, John Johansen,
	Andreas Gruenbacher

[-- Attachment #1: apparmor-lsm.diff --]
[-- Type: text/plain, Size: 21428 bytes --]

Module parameters, LSM hooks, initialization and teardown.

Signed-off-by: John Johansen <jjohansen@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>

---
 security/apparmor/lsm.c |  815 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 815 insertions(+)

--- /dev/null
+++ b/security/apparmor/lsm.c
@@ -0,0 +1,815 @@
+/*
+ *	Copyright (C) 1998-2007 Novell/SUSE
+ *
+ *	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, version 2 of the
+ *	License.
+ *
+ *	AppArmor LSM interface
+ */
+
+#include <linux/security.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/ctype.h>
+#include <linux/sysctl.h>
+#include <linux/audit.h>
+
+#include "apparmor.h"
+#include "inline.h"
+
+/* Boot time disable flag */
+int apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE;
+
+static int __init apparmor_enabled_setup(char *str)
+{
+	apparmor_enabled = simple_strtol(str, NULL, 0);
+	return 1;
+}
+__setup("apparmor=", apparmor_enabled_setup);
+
+
+static int param_set_aabool(const char *val, struct kernel_param *kp);
+static int param_get_aabool(char *buffer, struct kernel_param *kp);
+#define param_check_aabool(name, p) __param_check(name, p, int)
+
+static int param_set_aauint(const char *val, struct kernel_param *kp);
+static int param_get_aauint(char *buffer, struct kernel_param *kp);
+#define param_check_aauint(name, p) __param_check(name, p, int)
+
+/* Flag values, also controllable via /sys/module/apparmor/parameters
+ * We define special types as we want to do additional mediation.
+ *
+ * Complain mode -- in complain mode access failures result in auditing only
+ * and task is allowed access.  audit events are processed by userspace to
+ * generate policy.  Default is 'enforce' (0).
+ * Value is also togglable per profile and referenced when global value is
+ * enforce.
+ */
+int apparmor_complain = 0;
+module_param_named(complain, apparmor_complain, aabool, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(apparmor_complain, "Toggle AppArmor complain mode");
+
+/* Debug mode */
+int apparmor_debug = 0;
+module_param_named(debug, apparmor_debug, aabool, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(apparmor_debug, "Toggle AppArmor debug mode");
+
+/* Audit mode */
+int apparmor_audit = 0;
+module_param_named(audit, apparmor_audit, aabool, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(apparmor_audit, "Toggle AppArmor audit mode");
+
+/* Syscall logging mode */
+int apparmor_logsyscall = 0;
+module_param_named(logsyscall, apparmor_logsyscall, aabool, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(apparmor_logsyscall, "Toggle AppArmor logsyscall mode");
+
+/* Maximum pathname length before accesses will start getting rejected */
+unsigned int apparmor_path_max = 2 * PATH_MAX;
+module_param_named(path_max, apparmor_path_max, aauint, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(apparmor_path_max, "Maximum pathname length allowed");
+
+static int param_set_aabool(const char *val, struct kernel_param *kp)
+{
+	if (aa_task_context(current))
+		return -EPERM;
+	return param_set_bool(val, kp);
+}
+
+static int param_get_aabool(char *buffer, struct kernel_param *kp)
+{
+	if (aa_task_context(current))
+		return -EPERM;
+	return param_get_bool(buffer, kp);
+}
+
+static int param_set_aauint(const char *val, struct kernel_param *kp)
+{
+	if (aa_task_context(current))
+		return -EPERM;
+	return param_set_uint(val, kp);
+}
+
+static int param_get_aauint(char *buffer, struct kernel_param *kp)
+{
+	if (aa_task_context(current))
+		return -EPERM;
+	return param_get_uint(buffer, kp);
+}
+
+static int aa_reject_syscall(struct task_struct *task, gfp_t flags,
+			     const char *name)
+{
+	struct aa_profile *profile = aa_get_profile(task);
+	int error = 0;
+
+	if (profile) {
+		error = aa_audit_syscallreject(profile, flags, name);
+		aa_put_profile(profile);
+	}
+
+	return error;
+}
+
+static int apparmor_ptrace(struct task_struct *parent,
+			   struct task_struct *child)
+{
+	struct aa_task_context *cxt;
+	int error = 0;
+
+	/*
+	 * parent can ptrace child when
+	 * - parent is unconfined
+	 * - parent & child are in the same namespace &&
+	 *   - parent is in complain mode
+	 *   - parent and child are confined by the same profile
+	 *   - parent profile has CAP_SYS_PTRACE
+	 */
+
+	rcu_read_lock();
+	cxt = aa_task_context(parent);
+	if (cxt) {
+		if (parent->nsproxy != child->nsproxy) {
+			struct aa_audit sa;
+			memset(&sa, 0, sizeof(sa));
+			sa.operation = "ptrace";
+			sa.gfp_mask = GFP_ATOMIC;
+			sa.parent = parent->pid;
+			sa.task = child->pid;
+			sa.info = "different namespaces";
+			aa_audit_reject(cxt->profile, &sa);
+			error = -EPERM;
+		} else {
+			struct aa_task_context *child_cxt =
+				aa_task_context(child);
+
+			error = aa_may_ptrace(cxt, child_cxt ?
+						   child_cxt->profile : NULL);
+			if (PROFILE_COMPLAIN(cxt->profile)) {
+				struct aa_audit sa;
+				memset(&sa, 0, sizeof(sa));
+				sa.operation = "ptrace";
+				sa.gfp_mask = GFP_ATOMIC;
+				sa.parent = parent->pid;
+				sa.task = child->pid;
+				aa_audit_hint(cxt->profile, &sa);
+			}
+		}
+	}
+	rcu_read_unlock();
+
+	return error;
+}
+
+static int apparmor_capable(struct task_struct *task, int cap)
+{
+	int error;
+
+	/* cap_capable returns 0 on success, else -EPERM */
+	error = cap_capable(task, cap);
+
+	if (!error) {
+		struct aa_task_context *cxt;
+
+		rcu_read_lock();
+		cxt = aa_task_context(task);
+		if (cxt)
+			error = aa_capability(cxt, cap);
+		rcu_read_unlock();
+	}
+
+	return error;
+}
+
+static int apparmor_sysctl(struct ctl_table *table, int op)
+{
+	struct aa_profile *profile = aa_get_profile(current);
+	int error = 0;
+
+	if (profile) {
+		char *buffer, *name;
+		int mask;
+
+		mask = 0;
+		if (op & 4)
+			mask |= MAY_READ;
+		if (op & 2)
+			mask |= MAY_WRITE;
+
+		error = -ENOMEM;
+		buffer = (char*)__get_free_page(GFP_KERNEL);
+		if (!buffer)
+			goto out;
+		name = sysctl_pathname(table, buffer, PAGE_SIZE);
+		if (name && name - buffer >= 5) {
+			name -= 5;
+			memcpy(name, "/proc", 5);
+			error = aa_perm_path(profile, "sysctl", name, mask, 0);
+		}
+		free_page((unsigned long)buffer);
+	}
+
+out:
+	return error;
+}
+
+static int apparmor_bprm_set_security(struct linux_binprm *bprm)
+{
+	/* handle capability bits with setuid, etc */
+	cap_bprm_set_security(bprm);
+	/* already set based on script name */
+	if (bprm->sh_bang)
+		return 0;
+	return aa_register(bprm);
+}
+
+static int apparmor_bprm_secureexec(struct linux_binprm *bprm)
+{
+	int ret = cap_bprm_secureexec(bprm);
+
+	if (!ret && (unsigned long)bprm->security & AA_SECURE_EXEC_NEEDED) {
+		AA_DEBUG("%s: secureexec required for %s\n",
+			 __FUNCTION__, bprm->filename);
+		ret = 1;
+	}
+
+	return ret;
+}
+
+static int apparmor_sb_mount(char *dev_name, struct nameidata *nd, char *type,
+			      unsigned long flags, void *data)
+{
+	return aa_reject_syscall(current, GFP_KERNEL, "mount");
+}
+
+static int apparmor_umount(struct vfsmount *mnt, int flags)
+{
+	return aa_reject_syscall(current, GFP_KERNEL, "umount");
+}
+
+static int apparmor_inode_mkdir(struct inode *dir, struct dentry *dentry,
+				struct vfsmount *mnt, int mask)
+{
+	struct aa_profile *profile;
+	int error = 0;
+
+	if (!mnt || !mediated_filesystem(dir))
+		goto out;
+
+	profile = aa_get_profile(current);
+
+	if (profile)
+		error = aa_perm_dir(profile, "inode_mkdir", dentry, mnt,
+				    MAY_WRITE);
+
+	aa_put_profile(profile);
+
+out:
+	return error;
+}
+
+static int apparmor_inode_rmdir(struct inode *dir, struct dentry *dentry,
+				struct vfsmount *mnt)
+{
+	struct aa_profile *profile;
+	int error = 0;
+
+	if (!mnt || !mediated_filesystem(dir))
+		goto out;
+
+	profile = aa_get_profile(current);
+
+	if (profile)
+		error = aa_perm_dir(profile, "inode_rmdir", dentry, mnt,
+				    MAY_WRITE);
+
+	aa_put_profile(profile);
+
+out:
+	return error;
+}
+
+static int aa_permission(const char *operation, struct inode *inode,
+			 struct dentry *dentry, struct vfsmount *mnt,
+			 int mask, int check)
+{
+	int error = 0;
+
+	if (mnt && mediated_filesystem(inode)) {
+		struct aa_profile *profile;
+
+		profile = aa_get_profile(current);
+		if (profile)
+			error = aa_perm(profile, operation, dentry, mnt, mask,
+					check);
+		aa_put_profile(profile);
+	}
+	return error;
+}
+
+static inline int aa_mask_permissions(int mask)
+{
+	if (mask & MAY_APPEND)
+		mask &= (MAY_READ | MAY_APPEND | MAY_EXEC);
+	else
+		mask &= (MAY_READ | MAY_WRITE | MAY_EXEC);
+	return mask;
+}
+
+static int apparmor_inode_create(struct inode *dir, struct dentry *dentry,
+				 struct vfsmount *mnt, int mask)
+{
+	/* FIXME: may move to MAY_APPEND later */
+	return aa_permission("inode_create", dir, dentry, mnt, MAY_WRITE, 0);
+}
+
+static int apparmor_inode_link(struct dentry *old_dentry,
+			       struct vfsmount *old_mnt, struct inode *dir,
+			       struct dentry *new_dentry,
+			       struct vfsmount *new_mnt)
+{
+	int error = 0;
+	struct aa_profile *profile;
+
+	if (!old_mnt || !new_mnt || !mediated_filesystem(dir))
+		goto out;
+
+	profile = aa_get_profile(current);
+
+	if (profile)
+		error = aa_link(profile, new_dentry, new_mnt,
+				old_dentry, old_mnt);
+
+	aa_put_profile(profile);
+
+out:
+	return error;
+}
+
+static int apparmor_inode_unlink(struct inode *dir, struct dentry *dentry,
+				 struct vfsmount *mnt)
+{
+	int check = 0;
+
+	if (S_ISDIR(dentry->d_inode->i_mode))
+		check |= AA_CHECK_DIR;
+	return aa_permission("inode_unlink", dir, dentry, mnt, MAY_WRITE,
+			     check);
+}
+
+static int apparmor_inode_symlink(struct inode *dir, struct dentry *dentry,
+				  struct vfsmount *mnt, const char *old_name)
+{
+	return aa_permission("inode_symlink", dir, dentry, mnt, MAY_WRITE, 0);
+}
+
+static int apparmor_inode_mknod(struct inode *dir, struct dentry *dentry,
+				struct vfsmount *mnt, int mode, dev_t dev)
+{
+	return aa_permission("inode_mknod", dir, dentry, mnt, MAY_WRITE, 0);
+}
+
+static int apparmor_inode_rename(struct inode *old_dir,
+				 struct dentry *old_dentry,
+				 struct vfsmount *old_mnt,
+				 struct inode *new_dir,
+				 struct dentry *new_dentry,
+				 struct vfsmount *new_mnt)
+{
+	struct aa_profile *profile;
+	int error = 0;
+
+	if ((!old_mnt && !new_mnt) || !mediated_filesystem(old_dir))
+		goto out;
+
+	profile = aa_get_profile(current);
+
+	if (profile) {
+		struct inode *inode = old_dentry->d_inode;
+		int check = 0;
+
+		if (inode && S_ISDIR(inode->i_mode))
+			check |= AA_CHECK_DIR;
+		if (old_mnt)
+			error = aa_perm(profile, "inode_rename", old_dentry,
+					old_mnt, MAY_READ | MAY_WRITE, check);
+
+		if (!error && new_mnt) {
+			error = aa_perm(profile, "inode_rename", new_dentry,
+					new_mnt, MAY_WRITE, check);
+		}
+	}
+
+	aa_put_profile(profile);
+
+out:
+	return error;
+}
+
+static int apparmor_inode_permission(struct inode *inode, int mask,
+				     struct nameidata *nd)
+{
+	int check = 0;
+
+	if (!nd || nd->flags & (LOOKUP_PARENT | LOOKUP_CONTINUE))
+		return 0;
+	mask = aa_mask_permissions(mask);
+	if (S_ISDIR(inode->i_mode)) {
+		check |= AA_CHECK_DIR;
+		/* allow traverse accesses to directories */
+		mask &= ~MAY_EXEC;
+	}
+	return aa_permission("inode_permission", inode, nd->path.dentry,
+			     nd->path.mnt, mask, check);
+}
+
+static int apparmor_inode_setattr(struct dentry *dentry, struct vfsmount *mnt,
+				  struct iattr *iattr)
+{
+	int error = 0;
+
+	if (!mnt)
+		goto out;
+
+	if (mediated_filesystem(dentry->d_inode)) {
+		struct aa_profile *profile;
+
+		profile = aa_get_profile(current);
+		/*
+		 * Mediate any attempt to change attributes of a file
+		 * (chmod, chown, chgrp, etc)
+		 */
+		if (profile)
+			error = aa_attr(profile, dentry, mnt, iattr);
+
+		aa_put_profile(profile);
+	}
+
+out:
+	return error;
+}
+
+static int aa_xattr_permission(struct dentry *dentry, struct vfsmount *mnt,
+			       const char *operation, int mask,
+			       struct file *file)
+{
+	int error = 0;
+
+	if (mnt && mediated_filesystem(dentry->d_inode)) {
+		struct aa_profile *profile = aa_get_profile(current);
+		int check = file ? AA_CHECK_FD : 0;
+
+		if (profile)
+			error = aa_perm_xattr(profile, operation, dentry, mnt,
+					      mask, check);
+		aa_put_profile(profile);
+	}
+
+	return error;
+}
+
+static int apparmor_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt,
+				   char *name, void *value, size_t size,
+				   int flags, struct file *file)
+{
+	return aa_xattr_permission(dentry, mnt, "xattr set", MAY_WRITE, file);
+}
+
+static int apparmor_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt,
+				   char *name, struct file *file)
+{
+	return aa_xattr_permission(dentry, mnt, "xattr get", MAY_READ, file);
+}
+
+static int apparmor_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt,
+				    struct file *file)
+{
+	return aa_xattr_permission(dentry, mnt, "xattr list", MAY_READ, file);
+}
+
+static int apparmor_inode_removexattr(struct dentry *dentry,
+				      struct vfsmount *mnt, char *name,
+				      struct file *file)
+{
+	return aa_xattr_permission(dentry, mnt, "xattr remove", MAY_WRITE,
+				   file);
+}
+
+static int aa_file_permission(const char *op, struct file *file, int mask)
+{
+	struct aa_profile *profile;
+	struct aa_profile *file_profile = (struct aa_profile*)file->f_security;
+	int error = 0;
+
+	if (!file_profile)
+		goto out;
+
+	/*
+	 * If this file was opened under a different profile, we
+	 * revalidate the access against the current profile.
+	 */
+	profile = aa_get_profile(current);
+	if (profile && (file_profile != profile || mask & AA_MAY_LOCK)) {
+		struct dentry *dentry = file->f_dentry;
+		struct vfsmount *mnt = file->f_vfsmnt;
+		struct inode *inode = dentry->d_inode;
+		int check = AA_CHECK_FD;
+
+		/*
+		 * FIXME: We should remember which profiles we revalidated
+		 *	  against.
+		 */
+		if (S_ISDIR(inode->i_mode))
+			check |= AA_CHECK_DIR;
+		error = aa_permission(op, inode, dentry, mnt, mask, check);
+	}
+	aa_put_profile(profile);
+
+out:
+	return error;
+}
+
+static int apparmor_file_permission(struct file *file, int mask)
+{
+	return aa_file_permission("file_permission", file,
+				  aa_mask_permissions(mask));
+}
+
+static inline int apparmor_file_lock (struct file *file, unsigned int cmd)
+{
+	int mask = AA_MAY_LOCK;
+	if (cmd == F_WRLCK)
+		mask |= MAY_WRITE;
+	return aa_file_permission("file_lock", file, mask);
+}
+
+static int apparmor_file_alloc_security(struct file *file)
+{
+	struct aa_profile *profile;
+
+	profile = aa_get_profile(current);
+	if (profile)
+		file->f_security = profile;
+
+	return 0;
+}
+
+static void apparmor_file_free_security(struct file *file)
+{
+	struct aa_profile *file_profile = (struct aa_profile*)file->f_security;
+
+	aa_put_profile(file_profile);
+}
+
+static inline int aa_mmap(struct file *file, const char *operation,
+			  unsigned long prot, unsigned long flags)
+{
+	struct dentry *dentry;
+	int mask = 0;
+
+	if (!file || !file->f_security)
+		return 0;
+
+	if (prot & PROT_READ)
+		mask |= MAY_READ;
+	/* Private mappings don't require write perms since they don't
+	 * write back to the files */
+	if ((prot & PROT_WRITE) && !(flags & MAP_PRIVATE))
+		mask |= MAY_WRITE;
+	if (prot & PROT_EXEC)
+		mask |= AA_EXEC_MMAP;
+
+	dentry = file->f_dentry;
+	return aa_permission(operation, dentry->d_inode, dentry,
+			     file->f_vfsmnt, mask, AA_CHECK_FD);
+}
+
+static int apparmor_file_mmap(struct file *file, unsigned long reqprot,
+			      unsigned long prot, unsigned long flags,
+			      unsigned long addr, unsigned long addr_only)
+{
+	return aa_mmap(file, "file_mmap", prot, flags);
+}
+
+static int apparmor_file_mprotect(struct vm_area_struct *vma,
+				  unsigned long reqprot, unsigned long prot)
+{
+	return aa_mmap(vma->vm_file, "file_mprotect", prot,
+		       !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0);
+}
+
+static int apparmor_task_alloc_security(struct task_struct *task)
+{
+	return aa_clone(task);
+}
+
+/*
+ * Called from IRQ context from RCU callback.
+ */
+static void apparmor_task_free_security(struct task_struct *task)
+{
+	aa_release(task);
+}
+
+static int apparmor_getprocattr(struct task_struct *task, char *name,
+				char **value)
+{
+	unsigned len;
+	int error;
+	struct aa_profile *profile;
+
+	/* AppArmor only supports the "current" process attribute */
+	if (strcmp(name, "current") != 0)
+		return -EINVAL;
+
+	/* must be task querying itself or admin */
+	if (current != task && !capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	profile = aa_get_profile(task);
+	error = aa_getprocattr(profile, value, &len);
+	aa_put_profile(profile);
+	if (!error)
+		error = len;
+
+	return error;
+}
+
+static int apparmor_setprocattr(struct task_struct *task, char *name,
+				void *value, size_t size)
+{
+	char *command, *args;
+	int error;
+
+	if (strcmp(name, "current") != 0 || size == 0 || size >= PAGE_SIZE)
+		return -EINVAL;
+	args = value;
+	args[size] = '\0';
+	args = strstrip(args);
+	command = strsep(&args, " ");
+	if (!args)
+		return -EINVAL;
+	while (isspace(*args))
+		args++;
+	if (!*args)
+		return -EINVAL;
+
+	if (strcmp(command, "changehat") == 0) {
+		if (current != task)
+			return -EACCES;
+		error = aa_setprocattr_changehat(args);
+	} else if (strcmp(command, "changeprofile") == 0) {
+		if (current != task)
+			return -EACCES;
+		error = aa_setprocattr_changeprofile(args);
+	} else if (strcmp(command, "setprofile") == 0) {
+		struct aa_profile *profile;
+
+		/* Only an unconfined process with admin capabilities
+		 * may change the profile of another task.
+		 */
+
+		if (!capable(CAP_SYS_ADMIN))
+			return -EACCES;
+
+		profile = aa_get_profile(current);
+		if (profile) {
+			struct aa_audit sa;
+			memset(&sa, 0, sizeof(sa));
+			sa.operation = "profile_set";
+			sa.gfp_mask = GFP_KERNEL;
+			sa.task = task->pid;
+			sa.info = "from confined process";
+			aa_audit_reject(profile, &sa);
+			aa_put_profile(profile);
+			return -EACCES;
+		}
+		error = aa_setprocattr_setprofile(task, args);
+	} else {
+		struct aa_audit sa;
+		memset(&sa, 0, sizeof(sa));
+		sa.operation = "setprocattr";
+		sa.gfp_mask = GFP_KERNEL;
+		sa.info = "invalid command";
+		sa.name = command;
+		sa.task = task->pid;
+		aa_audit_reject(NULL, &sa);
+		return -EINVAL;
+	}
+
+	if (!error)
+		error = size;
+	return error;
+}
+
+struct security_operations apparmor_ops = {
+	.ptrace =			apparmor_ptrace,
+	.capget =			cap_capget,
+	.capset_check =			cap_capset_check,
+	.capset_set =			cap_capset_set,
+	.sysctl =			apparmor_sysctl,
+	.capable =			apparmor_capable,
+	.syslog =			cap_syslog,
+
+	.netlink_send =			cap_netlink_send,
+	.netlink_recv =			cap_netlink_recv,
+
+	.bprm_apply_creds =		cap_bprm_apply_creds,
+	.bprm_set_security =		apparmor_bprm_set_security,
+	.bprm_secureexec =		apparmor_bprm_secureexec,
+
+	.sb_mount =			apparmor_sb_mount,
+	.sb_umount =			apparmor_umount,
+
+	.inode_mkdir =			apparmor_inode_mkdir,
+	.inode_rmdir =			apparmor_inode_rmdir,
+	.inode_create =			apparmor_inode_create,
+	.inode_link =			apparmor_inode_link,
+	.inode_unlink =			apparmor_inode_unlink,
+	.inode_symlink =		apparmor_inode_symlink,
+	.inode_mknod =			apparmor_inode_mknod,
+	.inode_rename =			apparmor_inode_rename,
+	.inode_permission =		apparmor_inode_permission,
+	.inode_setattr =		apparmor_inode_setattr,
+	.inode_setxattr =		apparmor_inode_setxattr,
+	.inode_getxattr =		apparmor_inode_getxattr,
+	.inode_listxattr =		apparmor_inode_listxattr,
+	.inode_removexattr =		apparmor_inode_removexattr,
+	.file_permission =		apparmor_file_permission,
+	.file_alloc_security =		apparmor_file_alloc_security,
+	.file_free_security =		apparmor_file_free_security,
+	.file_mmap =			apparmor_file_mmap,
+	.file_mprotect =		apparmor_file_mprotect,
+	.file_lock =			apparmor_file_lock,
+
+	.task_alloc_security =		apparmor_task_alloc_security,
+	.task_free_security =		apparmor_task_free_security,
+	.task_post_setuid =		cap_task_post_setuid,
+	.task_reparent_to_init =	cap_task_reparent_to_init,
+
+	.getprocattr =			apparmor_getprocattr,
+	.setprocattr =			apparmor_setprocattr,
+};
+
+static void info_message(const char *str)
+{
+	struct aa_audit sa;
+	memset(&sa, 0, sizeof(sa));
+	sa.gfp_mask = GFP_KERNEL;
+	sa.info = str;
+	printk(KERN_INFO "AppArmor: %s", str);
+	aa_audit_message(NULL, &sa, AUDIT_APPARMOR_STATUS);
+}
+
+static int __init apparmor_init(void)
+{
+	int error;
+
+	if (!apparmor_enabled) {
+		info_message("AppArmor disabled by boottime parameter\n");
+		return 0;
+	}
+
+	if ((error = create_apparmorfs())) {
+		AA_ERROR("Unable to activate AppArmor filesystem\n");
+		goto createfs_out;
+	}
+
+	if ((error = alloc_default_namespace())){
+		AA_ERROR("Unable to allocate default profile namespace\n");
+		goto alloc_out;
+	}
+
+ 	if ((error = register_security(&apparmor_ops))) {
+		AA_ERROR("Unable to register AppArmor\n");
+		goto register_security_out;
+	}
+
+	if (apparmor_complain)
+		info_message("AppArmor initialized: complainmode enabled");
+	else
+		info_message("AppArmor initialized");
+
+	return error;
+
+register_security_out:
+	free_default_namespace();
+
+alloc_out:
+ 	destroy_apparmorfs();
+
+createfs_out:
+	return error;
+
+}
+
+module_init(apparmor_init);
+
+MODULE_DESCRIPTION("AppArmor process confinement");
+MODULE_AUTHOR("Novell/Immunix, http://bugs.opensuse.org");
+MODULE_LICENSE("GPL");

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 43/47] AppArmor: Profile loading and manipulation, pathname matching
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (41 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 42/47] AppArmor: Module and LSM hooks John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 44/47] AppArmor: all the rest John, Johansen
                   ` (3 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, John Johansen,
	Andreas Gruenbacher

[-- Attachment #1: apparmor-module_interface.diff --]
[-- Type: text/plain, Size: 30989 bytes --]

Pathname matching, transition table loading, profile loading and
manipulation.

Signed-off-by: John Johansen <jjohansen@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>

---
 security/apparmor/match.c            |  299 +++++++++++++
 security/apparmor/match.h            |   85 +++
 security/apparmor/module_interface.c |  805 +++++++++++++++++++++++++++++++++++
 3 files changed, 1189 insertions(+)

--- /dev/null
+++ b/security/apparmor/match.c
@@ -0,0 +1,299 @@
+/*
+ *	Copyright (C) 2007 Novell/SUSE
+ *
+ *	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, version 2 of the
+ *	License.
+ *
+ *	Regular expression transition table matching
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include "apparmor.h"
+#include "match.h"
+
+static struct table_header *unpack_table(void *blob, size_t bsize)
+{
+	struct table_header *table = NULL;
+	struct table_header th;
+	size_t tsize;
+
+	if (bsize < sizeof(struct table_header))
+		goto out;
+
+	th.td_id = be16_to_cpu(*(u16 *) (blob));
+	th.td_flags = be16_to_cpu(*(u16 *) (blob + 2));
+	th.td_lolen = be32_to_cpu(*(u32 *) (blob + 8));
+	blob += sizeof(struct table_header);
+
+	if (!(th.td_flags == YYTD_DATA16 || th.td_flags == YYTD_DATA32 ||
+		th.td_flags == YYTD_DATA8))
+		goto out;
+
+	tsize = table_size(th.td_lolen, th.td_flags);
+	if (bsize < tsize)
+		goto out;
+
+	table = kmalloc(tsize, GFP_KERNEL);
+	if (table) {
+		*table = th;
+		if (th.td_flags == YYTD_DATA8)
+			UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
+				     u8, byte_to_byte);
+		else if (th.td_flags == YYTD_DATA16)
+			UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
+				     u16, be16_to_cpu);
+		else
+			UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
+				     u32, be32_to_cpu);
+	}
+
+out:
+	return table;
+}
+
+int unpack_dfa(struct aa_dfa *dfa, void *blob, size_t size)
+{
+	int hsize, i;
+	int error = -ENOMEM;
+
+	/* get dfa table set header */
+	if (size < sizeof(struct table_set_header))
+		goto fail;
+
+	if (ntohl(*(u32 *)blob) != YYTH_MAGIC)
+		goto fail;
+
+	hsize = ntohl(*(u32 *)(blob + 4));
+	if (size < hsize)
+		goto fail;
+
+	blob += hsize;
+	size -= hsize;
+
+	error = -EPROTO;
+	while (size > 0) {
+		struct table_header *table;
+		table = unpack_table(blob, size);
+		if (!table)
+			goto fail;
+
+		switch(table->td_id) {
+		case YYTD_ID_ACCEPT:
+		case YYTD_ID_BASE:
+			dfa->tables[table->td_id - 1] = table;
+			if (table->td_flags != YYTD_DATA32)
+				goto fail;
+			break;
+		case YYTD_ID_DEF:
+		case YYTD_ID_NXT:
+		case YYTD_ID_CHK:
+			dfa->tables[table->td_id - 1] = table;
+			if (table->td_flags != YYTD_DATA16)
+				goto fail;
+			break;
+		case YYTD_ID_EC:
+			dfa->tables[table->td_id - 1] = table;
+			if (table->td_flags != YYTD_DATA8)
+				goto fail;
+			break;
+		default:
+			kfree(table);
+			goto fail;
+		}
+
+		blob += table_size(table->td_lolen, table->td_flags);
+		size -= table_size(table->td_lolen, table->td_flags);
+	}
+
+	return 0;
+
+fail:
+	for (i = 0; i < ARRAY_SIZE(dfa->tables); i++) {
+		if (dfa->tables[i]) {
+			kfree(dfa->tables[i]);
+			dfa->tables[i] = NULL;
+		}
+	}
+	return error;
+}
+
+/**
+ * verify_dfa - verify that all the transitions and states in the dfa tables
+ *              are in bounds.
+ * @dfa: dfa to test
+ *
+ * assumes dfa has gone through the verification done by unpacking
+ */
+int verify_dfa(struct aa_dfa *dfa)
+{
+	size_t i, state_count, trans_count;
+	int error = -EPROTO;
+
+	/* check that required tables exist */
+	if (!(dfa->tables[YYTD_ID_ACCEPT -1 ] &&
+	      dfa->tables[YYTD_ID_DEF - 1] &&
+	      dfa->tables[YYTD_ID_BASE - 1] &&
+	      dfa->tables[YYTD_ID_NXT - 1] &&
+	      dfa->tables[YYTD_ID_CHK - 1]))
+		goto out;
+
+	/* accept.size == default.size == base.size */
+	state_count = dfa->tables[YYTD_ID_BASE - 1]->td_lolen;
+	if (!(state_count == dfa->tables[YYTD_ID_DEF - 1]->td_lolen &&
+	      state_count == dfa->tables[YYTD_ID_ACCEPT - 1]->td_lolen))
+		goto out;
+
+	/* next.size == chk.size */
+	trans_count = dfa->tables[YYTD_ID_NXT - 1]->td_lolen;
+	if (trans_count != dfa->tables[YYTD_ID_CHK - 1]->td_lolen)
+		goto out;
+
+	/* if equivalence classes then its table size must be 256 */
+	if (dfa->tables[YYTD_ID_EC - 1] &&
+	    dfa->tables[YYTD_ID_EC - 1]->td_lolen != 256)
+		goto out;
+
+	for (i = 0; i < state_count; i++) {
+		if (DEFAULT_TABLE(dfa)[i] >= state_count)
+			goto out;
+		if (BASE_TABLE(dfa)[i] >= trans_count + 256)
+			goto out;
+	}
+
+	for (i = 0; i < trans_count ; i++) {
+		if (NEXT_TABLE(dfa)[i] >= state_count)
+			goto out;
+		if (CHECK_TABLE(dfa)[i] >= state_count)
+			goto out;
+	}
+
+	/* verify accept permissions */
+	for (i = 0; i < state_count; i++) {
+		int mode = ACCEPT_TABLE(dfa)[i];
+
+		if (mode & ~AA_VALID_PERM_MASK) {
+			goto out;
+		}
+	}
+
+	error = 0;
+out:
+	return error;
+}
+
+struct aa_dfa *aa_match_alloc(void)
+{
+	return kzalloc(sizeof(struct aa_dfa), GFP_KERNEL);
+}
+
+void aa_match_free(struct aa_dfa *dfa)
+{
+	if (dfa) {
+		int i;
+
+		for (i = 0; i < ARRAY_SIZE(dfa->tables); i++)
+			kfree(dfa->tables[i]);
+	}
+	kfree(dfa);
+}
+
+/**
+ * aa_dfa_next_state - traverse @dfa to find state @str stops at
+ * @dfa: the dfa to match @str against
+ * @start: the state of the dfa to start matching in
+ * @str: the string to match against the dfa
+ *
+ * aa_dfa_next_state will match @str against the dfa and return the state it
+ * finished matching in. The final state can be used to look up the accepting
+ * label, or as the start state of a continuing match.
+ */
+unsigned int aa_dfa_next_state(struct aa_dfa *dfa, unsigned int start,
+			       const char *str)
+{
+	u16 *def = DEFAULT_TABLE(dfa);
+	u32 *base = BASE_TABLE(dfa);
+	u16 *next = NEXT_TABLE(dfa);
+	u16 *check = CHECK_TABLE(dfa);
+	unsigned int state = start, pos;
+
+	if (state == 0)
+		return 0;
+
+	/* current state is <state>, matching character *str */
+	if (dfa->tables[YYTD_ID_EC - 1]) {
+		u8 *equiv = EQUIV_TABLE(dfa);
+		while (*str) {
+			pos = base[state] + equiv[(u8)*str++];
+			if (check[pos] == state)
+				state = next[pos];
+			else
+				state = def[state];
+		}
+	} else {
+		while (*str) {
+			pos = base[state] + (u8)*str++;
+			if (check[pos] == state)
+				state = next[pos];
+			else
+				state = def[state];
+		}
+	}
+	return state;
+}
+
+/**
+ * aa_dfa_null_transition - step to next state after null character
+ * @dfa: the dfa to match against
+ * @start: the state of the dfa to start matching in
+ *
+ * aa_dfa_null_transition transitions to the next state after a null
+ * character which is not used in standard matching and is only
+ * used to seperate pairs.
+ */
+unsigned int aa_dfa_null_transition(struct aa_dfa *dfa, unsigned int start)
+{
+	return aa_dfa_next_state(dfa, start, "//");
+}
+
+/**
+ * aa_dfa_match - find accept perm for @str in @dfa
+ * @dfa: the dfa to match @str against
+ * @str: the string to match against the dfa
+ *
+ * aa_dfa_match will match @str and return the accept perms for the
+ * final state.
+ */
+unsigned int aa_dfa_match(struct aa_dfa *dfa, const char *str)
+{
+	return ACCEPT_TABLE(dfa)[aa_dfa_next_state(dfa, DFA_START, str)];
+}
+
+/**
+ * aa_match_state - find accept perm and state for @str in @dfa
+ * @dfa: the dfa to match @str against
+ * @start: the state to start the match from
+ * @str: the string to match against the dfa
+ * @final: the state that the match finished in
+ *
+ * aa_match_state will match @str and return the accept perms, and @final
+ * state, the match occured in.
+ */
+unsigned int aa_match_state(struct aa_dfa *dfa, unsigned int start,
+			    const char *str, unsigned int *final)
+{
+	unsigned int state;
+	if (dfa) {
+		state = aa_dfa_next_state(dfa, start, str);
+		if (final)
+			*final = state;
+		return ACCEPT_TABLE(dfa)[state];
+	}
+	if (final)
+		*final = 0;
+	return 0;
+}
+
--- /dev/null
+++ b/security/apparmor/match.h
@@ -0,0 +1,85 @@
+/*
+ *	Copyright (C) 2007 Novell/SUSE
+ *
+ *	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, version 2 of the
+ *	License.
+ *
+ *	AppArmor submodule (match) prototypes
+ */
+
+#ifndef __MATCH_H
+#define __MATCH_H
+
+#define DFA_START			1
+
+/**
+ * The format used for transition tables is based on the GNU flex table
+ * file format (--tables-file option; see Table File Format in the flex
+ * info pages and the flex sources for documentation). The magic number
+ * used in the header is 0x1B5E783D insted of 0xF13C57B1 though, because
+ * the YY_ID_CHK (check) and YY_ID_DEF (default) tables are used
+ * slightly differently (see the apparmor-parser package).
+ */
+
+#define YYTH_MAGIC	0x1B5E783D
+
+struct table_set_header {
+	u32		th_magic;	/* YYTH_MAGIC */
+	u32		th_hsize;
+	u32		th_ssize;
+	u16		th_flags;
+	char		th_version[];
+};
+
+#define	YYTD_ID_ACCEPT	1
+#define YYTD_ID_BASE	2
+#define YYTD_ID_CHK	3
+#define YYTD_ID_DEF	4
+#define YYTD_ID_EC	5
+#define YYTD_ID_META	6
+#define YYTD_ID_NXT	8
+
+
+#define YYTD_DATA8	1
+#define YYTD_DATA16	2
+#define YYTD_DATA32	4
+
+struct table_header {
+	u16		td_id;
+	u16		td_flags;
+	u32		td_hilen;
+	u32		td_lolen;
+	char		td_data[];
+};
+
+#define DEFAULT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_DEF - 1]->td_data))
+#define BASE_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_BASE - 1]->td_data))
+#define NEXT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_NXT - 1]->td_data))
+#define CHECK_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_CHK - 1]->td_data))
+#define EQUIV_TABLE(DFA) ((u8 *)((DFA)->tables[YYTD_ID_EC - 1]->td_data))
+#define ACCEPT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT - 1]->td_data))
+
+struct aa_dfa {
+	struct table_header *tables[YYTD_ID_NXT];
+};
+
+#define byte_to_byte(X) (X)
+
+#define UNPACK_ARRAY(TABLE, BLOB, LEN, TYPE, NTOHX) \
+	do { \
+		typeof(LEN) __i; \
+		TYPE *__t = (TYPE *) TABLE; \
+		TYPE *__b = (TYPE *) BLOB; \
+		for (__i = 0; __i < LEN; __i++) { \
+			__t[__i] = NTOHX(__b[__i]); \
+		} \
+	} while (0)
+
+static inline size_t table_size(size_t len, size_t el_size)
+{
+	return ALIGN(sizeof(struct table_header) + len * el_size, 8);
+}
+
+#endif /* __MATCH_H */
--- /dev/null
+++ b/security/apparmor/module_interface.c
@@ -0,0 +1,805 @@
+/*
+ *	Copyright (C) 1998-2007 Novell/SUSE
+ *
+ *	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, version 2 of the
+ *	License.
+ *
+ *	AppArmor userspace policy interface
+ */
+
+#include <asm/unaligned.h>
+
+#include "apparmor.h"
+#include "inline.h"
+
+/*
+ * This mutex is used to synchronize profile adds, replacements, and
+ * removals: we only allow one of these operations at a time.
+ * We do not use the profile list lock here in order to avoid blocking
+ * exec during those operations.  (Exec involves a profile list lookup
+ * for named-profile transitions.)
+ */
+DEFINE_MUTEX(aa_interface_lock);
+
+/*
+ * The AppArmor interface treats data as a type byte followed by the
+ * actual data.  The interface has the notion of a a named entry
+ * which has a name (AA_NAME typecode followed by name string) followed by
+ * the entries typecode and data.  Named types allow for optional
+ * elements and extensions to be added and tested for without breaking
+ * backwards compatability.
+ */
+
+enum aa_code {
+	AA_U8,
+	AA_U16,
+	AA_U32,
+	AA_U64,
+	AA_NAME,	/* same as string except it is items name */
+	AA_STRING,
+	AA_BLOB,
+	AA_STRUCT,
+	AA_STRUCTEND,
+	AA_LIST,
+	AA_LISTEND,
+	AA_ARRAY,
+	AA_ARRAYEND,
+};
+
+/*
+ * aa_ext is the read of the buffer containing the serialized profile.  The
+ * data is copied into a kernel buffer in apparmorfs and then handed off to
+ * the unpack routines.
+ */
+struct aa_ext {
+	void *start;
+	void *end;
+	void *pos;	/* pointer to current position in the buffer */
+	u32 version;
+	char *ns_name;
+};
+
+static inline int aa_inbounds(struct aa_ext *e, size_t size)
+{
+	return (size <= e->end - e->pos);
+}
+
+/**
+ * aa_u16_chunck - test and do bounds checking for a u16 size based chunk
+ * @e: serialized data read head
+ * @chunk: start address for chunk of data
+ *
+ * return the size of chunk found with the read head at the end of
+ * the chunk.
+ */
+static size_t aa_is_u16_chunk(struct aa_ext *e, char **chunk)
+{
+	void *pos = e->pos;
+	size_t size = 0;
+
+	if (!aa_inbounds(e, sizeof(u16)))
+		goto fail;
+	size = le16_to_cpu(get_unaligned((u16 *)e->pos));
+	e->pos += sizeof(u16);
+	if (!aa_inbounds(e, size))
+		goto fail;
+	*chunk = e->pos;
+	e->pos += size;
+	return size;
+
+fail:
+	e->pos = pos;
+	return 0;
+}
+
+static inline int aa_is_X(struct aa_ext *e, enum aa_code code)
+{
+	if (!aa_inbounds(e, 1))
+		return 0;
+	if (*(u8 *) e->pos != code)
+		return 0;
+	e->pos++;
+	return 1;
+}
+
+/**
+ * aa_is_nameX - check is the next element is of type X with a name of @name
+ * @e: serialized data extent information
+ * @code: type code
+ * @name: name to match to the serialized element.
+ *
+ * check that the next serialized data element is of type X and has a tag
+ * name @name.  If @name is specified then there must be a matching
+ * name element in the stream.  If @name is NULL any name element will be
+ * skipped and only the typecode will be tested.
+ * returns 1 on success (both type code and name tests match) and the read
+ * head is advanced past the headers
+ * returns %0 if either match failes, the read head does not move
+ */
+static int aa_is_nameX(struct aa_ext *e, enum aa_code code, const char *name)
+{
+	void *pos = e->pos;
+	/*
+	 * Check for presence of a tagname, and if present name size
+	 * AA_NAME tag value is a u16.
+	 */
+	if (aa_is_X(e, AA_NAME)) {
+		char *tag;
+		size_t size = aa_is_u16_chunk(e, &tag);
+		/* if a name is specified it must match. otherwise skip tag */
+		if (name && (!size || strcmp(name, tag)))
+			goto fail;
+	} else if (name) {
+		/* if a name is specified and there is no name tag fail */
+		goto fail;
+	}
+
+	/* now check if type code matches */
+	if (aa_is_X(e, code))
+		return 1;
+
+fail:
+	e->pos = pos;
+	return 0;
+}
+
+static int aa_is_u16(struct aa_ext *e, u16 *data, const char *name)
+{
+	void *pos = e->pos;
+	if (aa_is_nameX(e, AA_U16, name)) {
+		if (!aa_inbounds(e, sizeof(u16)))
+			goto fail;
+		if (data)
+			*data = le16_to_cpu(get_unaligned((u16 *)e->pos));
+		e->pos += sizeof(u16);
+		return 1;
+	}
+fail:
+	e->pos = pos;
+	return 0;
+}
+
+static int aa_is_u32(struct aa_ext *e, u32 *data, const char *name)
+{
+	void *pos = e->pos;
+	if (aa_is_nameX(e, AA_U32, name)) {
+		if (!aa_inbounds(e, sizeof(u32)))
+			goto fail;
+		if (data)
+			*data = le32_to_cpu(get_unaligned((u32 *)e->pos));
+		e->pos += sizeof(u32);
+		return 1;
+	}
+fail:
+	e->pos = pos;
+	return 0;
+}
+
+static size_t aa_is_array(struct aa_ext *e, const char *name)
+{
+	void *pos = e->pos;
+	if (aa_is_nameX(e, AA_ARRAY, name)) {
+		int size;
+		if (!aa_inbounds(e, sizeof(u16)))
+			goto fail;
+		size = (int) le16_to_cpu(get_unaligned((u16 *)e->pos));
+		e->pos += sizeof(u16);
+		return size;
+	}
+fail:
+	e->pos = pos;
+	return 0;
+}
+
+static size_t aa_is_blob(struct aa_ext *e, char **blob, const char *name)
+{
+	void *pos = e->pos;
+	if (aa_is_nameX(e, AA_BLOB, name)) {
+		u32 size;
+		if (!aa_inbounds(e, sizeof(u32)))
+			goto fail;
+		size = le32_to_cpu(get_unaligned((u32 *)e->pos));
+		e->pos += sizeof(u32);
+		if (aa_inbounds(e, (size_t) size)) {
+			* blob = e->pos;
+			e->pos += size;
+			return size;
+		}
+	}
+fail:
+	e->pos = pos;
+	return 0;
+}
+
+static int aa_is_dynstring(struct aa_ext *e, char **string, const char *name)
+{
+	char *src_str;
+	size_t size = 0;
+	void *pos = e->pos;
+	*string = NULL;
+	if (aa_is_nameX(e, AA_STRING, name) &&
+	    (size = aa_is_u16_chunk(e, &src_str))) {
+		char *str;
+		if (!(str = kmalloc(size, GFP_KERNEL)))
+			goto fail;
+		memcpy(str, src_str, size);
+		*string = str;
+	}
+
+	return size;
+
+fail:
+	e->pos = pos;
+	return 0;
+}
+
+/**
+ * aa_unpack_dfa - unpack a file rule dfa
+ * @e: serialized data extent information
+ *
+ * returns dfa or ERR_PTR
+ */
+struct aa_dfa *aa_unpack_dfa(struct aa_ext *e)
+{
+	char *blob = NULL;
+	size_t size, error = 0;
+	struct aa_dfa *dfa = NULL;
+
+	size = aa_is_blob(e, &blob, "aadfa");
+	if (size) {
+		dfa = aa_match_alloc();
+		if (dfa) {
+			/*
+			 * The dfa is aligned with in the blob to 8 bytes
+			 * from the beginning of the stream.
+			 */
+			size_t sz = blob - (char *) e->start;
+			size_t pad = ALIGN(sz, 8) - sz;
+			error = unpack_dfa(dfa, blob + pad, size - pad);
+			if (!error)
+				error = verify_dfa(dfa);
+		} else {
+			error = -ENOMEM;
+		}
+
+		if (error) {
+			aa_match_free(dfa);
+			dfa = ERR_PTR(error);
+		}
+	}
+
+	return dfa;
+}
+
+/**
+ * aa_unpack_profile - unpack a serialized profile
+ * @e: serialized data extent information
+ * @operation: operation profile is being unpacked for
+ */
+static struct aa_profile *aa_unpack_profile(struct aa_ext *e,
+					    const char *operation)
+{
+	struct aa_profile *profile = NULL;
+	struct aa_audit sa;
+
+	int error = -EPROTO;
+
+	profile = alloc_aa_profile();
+	if (!profile)
+		return ERR_PTR(-ENOMEM);
+
+	/* check that we have the right struct being passed */
+	if (!aa_is_nameX(e, AA_STRUCT, "profile"))
+		goto fail;
+	if (!aa_is_dynstring(e, &profile->name, NULL))
+		goto fail;
+
+	/* per profile debug flags (complain, audit) */
+	if (!aa_is_nameX(e, AA_STRUCT, "flags"))
+		goto fail;
+	if (!aa_is_u32(e, NULL, NULL))
+		goto fail;
+	if (!aa_is_u32(e, &(profile->flags.complain), NULL))
+		goto fail;
+	if (!aa_is_u32(e, &(profile->flags.audit), NULL))
+		goto fail;
+	if (!aa_is_nameX(e, AA_STRUCTEND, NULL))
+		goto fail;
+
+	if (!aa_is_u32(e, &(profile->capabilities), NULL))
+		goto fail;
+
+	/* get file rules */
+	profile->file_rules = aa_unpack_dfa(e);
+	if (IS_ERR(profile->file_rules)) {
+		error = PTR_ERR(profile->file_rules);
+		profile->file_rules = NULL;
+		goto fail;
+	}
+
+	if (!aa_is_nameX(e, AA_STRUCTEND, NULL))
+		goto fail;
+
+	return profile;
+
+fail:
+	memset(&sa, 0, sizeof(sa));
+	sa.operation = operation;
+	sa.gfp_mask = GFP_KERNEL;
+	sa.name = profile && profile->name ? profile->name : "unknown";
+	if (!sa.info)
+		sa.info = "failed to unpack profile";
+	aa_audit_status(NULL, &sa);
+
+	if (profile)
+		free_aa_profile(profile);
+
+	return ERR_PTR(error);
+}
+
+/**
+ * aa_verify_head - unpack serialized stream header
+ * @e: serialized data read head
+ * @operation: operation header is being verified for
+ *
+ * returns error or 0 if header is good
+ */
+static int aa_verify_header(struct aa_ext *e, const char *operation)
+{
+	/* get the interface version */
+	if (!aa_is_u32(e, &e->version, "version")) {
+		struct aa_audit sa;
+		memset(&sa, 0, sizeof(sa));
+		sa.operation = operation;
+		sa.gfp_mask = GFP_KERNEL;
+		sa.info = "invalid profile format";
+		aa_audit_status(NULL, &sa);
+		return -EPROTONOSUPPORT;
+	}
+
+	/* check that the interface version is currently supported */
+	if (e->version != 3) {
+		struct aa_audit sa;
+		memset(&sa, 0, sizeof(sa));
+		sa.operation = operation;
+		sa.gfp_mask = GFP_KERNEL;
+		sa.info = "unsupported interface version";
+		aa_audit_status(NULL, &sa);
+		return -EPROTONOSUPPORT;
+	}
+
+	/* read the namespace if present */
+	if (!aa_is_dynstring(e, &e->ns_name, "namespace")) {
+		e->ns_name = NULL;
+	}
+
+	return 0;
+}
+
+/**
+ * aa_add_profile - Unpack and add a new profile to the profile list
+ * @data: serialized data stream
+ * @size: size of the serialized data stream
+ */
+ssize_t aa_add_profile(void *data, size_t size)
+{
+	struct aa_profile *profile = NULL;
+	struct aa_namespace *ns = NULL;
+	struct aa_ext e = {
+		.start = data,
+		.end = data + size,
+		.pos = data,
+		.ns_name = NULL
+	};
+	ssize_t error = aa_verify_header(&e, "profile_load");
+	if (error)
+		return error;
+
+	profile = aa_unpack_profile(&e, "profile_load");
+	if (IS_ERR(profile))
+		return PTR_ERR(profile);
+
+	mutex_lock(&aa_interface_lock);
+	write_lock(&profile_ns_list_lock);
+	if (e.ns_name)
+		ns = __aa_find_namespace(e.ns_name, &profile_ns_list);
+	else
+		ns = default_namespace;
+	if (!ns) {
+		struct aa_namespace *new_ns;
+		write_unlock(&profile_ns_list_lock);
+		new_ns = alloc_aa_namespace(e.ns_name);
+		if (!new_ns) {
+			mutex_unlock(&aa_interface_lock);
+			return -ENOMEM;
+		}
+		write_lock(&profile_ns_list_lock);
+		ns = __aa_find_namespace(e.ns_name, &profile_ns_list);
+		if (!ns) {
+			list_add(&new_ns->list, &profile_ns_list);
+			ns = new_ns;
+		} else
+			free_aa_namespace(new_ns);
+	}
+
+	write_lock(&ns->lock);
+	if (__aa_find_profile(profile->name, &ns->profiles)) {
+		/* A profile with this name exists already. */
+		write_unlock(&ns->lock);
+		write_unlock(&profile_ns_list_lock);
+		mutex_unlock(&aa_interface_lock);
+		aa_put_profile(profile);
+		return -EEXIST;
+	}
+	profile->ns = aa_get_namespace(ns);
+	ns->profile_count++;
+	list_add(&profile->list, &ns->profiles);
+	write_unlock(&ns->lock);
+	write_unlock(&profile_ns_list_lock);
+	mutex_unlock(&aa_interface_lock);
+
+	return size;
+}
+
+/**
+ * task_replace - replace a task's profile
+ * @task: task to replace profile on
+ * @new_cxt: new aa_task_context to do replacement with
+ * @new_profile: new profile
+ */
+static inline void task_replace(struct task_struct *task,
+				struct aa_task_context *new_cxt,
+				struct aa_profile *new_profile)
+{
+	struct aa_task_context *cxt = aa_task_context(task);
+
+	AA_DEBUG("%s: replacing profile for task %d "
+		 "profile=%s (%p)\n",
+		 __FUNCTION__,
+		 cxt->task->pid,
+		 cxt->profile->name, cxt->profile);
+
+	aa_change_task_context(task, new_cxt, new_profile, cxt->cookie,
+			       cxt->previous_profile);
+}
+
+/**
+ * aa_replace_profile - replace a profile on the profile list
+ * @udata: serialized data stream
+ * @size: size of the serialized data stream
+ *
+ * unpack and replace a profile on the profile list and uses of that profile
+ * by any aa_task_context.  If the profile does not exist on the profile list
+ * it is added.  Return %0 or error.
+ */
+ssize_t aa_replace_profile(void *udata, size_t size)
+{
+	struct aa_profile *old_profile, *new_profile;
+	struct aa_namespace *ns;
+	struct aa_task_context *new_cxt;
+	struct aa_ext e = {
+		.start = udata,
+		.end = udata + size,
+		.pos = udata,
+		.ns_name = NULL
+	};
+
+	ssize_t error = aa_verify_header(&e, "profile_replace");
+	if (error)
+		return error;
+
+	new_profile = aa_unpack_profile(&e, "profile_replace");
+	if (IS_ERR(new_profile))
+		return PTR_ERR(new_profile);
+
+	mutex_lock(&aa_interface_lock);
+	write_lock(&profile_ns_list_lock);
+	if (e.ns_name)
+		ns = __aa_find_namespace(e.ns_name, &profile_ns_list);
+	else
+		ns = default_namespace;
+	if (!ns) {
+		struct aa_namespace *new_ns;
+		write_unlock(&profile_ns_list_lock);
+		new_ns = alloc_aa_namespace(e.ns_name);
+		if (!new_ns) {
+			mutex_unlock(&aa_interface_lock);
+			return -ENOMEM;
+		}
+		write_lock(&profile_ns_list_lock);
+		ns = __aa_find_namespace(e.ns_name, &profile_ns_list);
+		if (!ns) {
+			list_add(&new_ns->list, &profile_ns_list);
+			ns = new_ns;
+		} else
+			free_aa_namespace(new_ns);
+	}
+
+	write_lock(&ns->lock);
+	old_profile = __aa_find_profile(new_profile->name, &ns->profiles);
+	if (old_profile) {
+		lock_profile(old_profile);
+		old_profile->isstale = 1;
+		list_del_init(&old_profile->list);
+		unlock_profile(old_profile);
+		ns->profile_count--;
+	}
+	new_profile->ns = aa_get_namespace(ns);
+	ns->profile_count++;
+	list_add(&new_profile->list, &ns->profiles);
+	write_unlock(&ns->lock);
+	write_unlock(&profile_ns_list_lock);
+
+	if (!old_profile)
+		goto out;
+
+	/*
+	 * Replacement needs to allocate a new aa_task_context for each
+	 * task confined by old_profile.  To do this the profile locks
+	 * are only held when the actual switch is done per task.  While
+	 * looping to allocate a new aa_task_context the old_task list
+	 * may get shorter if tasks exit/change their profile but will
+	 * not get longer as new task will not use old_profile detecting
+	 * that is stale.
+	 */
+	do {
+		new_cxt = aa_alloc_task_context(GFP_KERNEL | __GFP_NOFAIL);
+
+		lock_both_profiles(old_profile, new_profile);
+		if (!list_empty(&old_profile->task_contexts)) {
+			struct task_struct *task =
+				list_entry(old_profile->task_contexts.next,
+					   struct aa_task_context, list)->task;
+			task_lock(task);
+			task_replace(task, new_cxt, new_profile);
+			task_unlock(task);
+			new_cxt = NULL;
+		}
+		unlock_both_profiles(old_profile, new_profile);
+	} while (!new_cxt);
+	aa_free_task_context(new_cxt);
+	aa_put_profile(old_profile);
+
+out:
+	mutex_unlock(&aa_interface_lock);
+	return size;
+}
+
+/**
+ * aa_remove_profile - remove a profile from the system
+ * @name: name of the profile to remove
+ * @size: size of the name
+ *
+ * remove a profile from the profile list and all aa_task_context references
+ * to said profile.
+ */
+ssize_t aa_remove_profile(char *name, size_t size)
+{
+	struct aa_namespace *ns;
+	struct aa_profile *profile;
+
+	mutex_lock(&aa_interface_lock);
+	write_lock(&profile_ns_list_lock);
+
+	if (name[0] == '/') {
+		ns = default_namespace;
+	} else {
+		char *split = strchr(name, ':');
+		if (!split)
+			goto noent;
+		*split = 0;
+		ns = __aa_find_namespace(name, &profile_ns_list);
+		name = split + 1;
+	}
+
+	if (!ns)
+		goto noent;
+	write_lock(&ns->lock);
+	profile = __aa_find_profile(name, &ns->profiles);
+	if (!profile) {
+		write_unlock(&ns->lock);
+		goto noent;
+	}
+
+	/* Remove the profile from each task context it is on. */
+	lock_profile(profile);
+	profile->isstale = 1;
+	aa_unconfine_tasks(profile);
+	list_del_init(&profile->list);
+	ns->profile_count--;
+	unlock_profile(profile);
+	/* Release the profile itself. */
+	write_unlock(&ns->lock);
+	/* check to see if the namespace has become stale */
+	if (ns != default_namespace && ns->profile_count == 0) {
+		list_del_init(&ns->list);
+		aa_put_namespace(ns);
+	}
+	write_unlock(&profile_ns_list_lock);
+	mutex_unlock(&aa_interface_lock);
+	aa_put_profile(profile);
+
+	return size;
+
+noent:
+	write_unlock(&profile_ns_list_lock);
+	mutex_unlock(&aa_interface_lock);
+	return -ENOENT;
+}
+
+/**
+ * free_aa_namespace_kref - free aa_namespace by kref (see aa_put_namespace)
+ * @kr: kref callback for freeing of a namespace
+ */
+void free_aa_namespace_kref(struct kref *kref)
+{
+	struct aa_namespace *ns=container_of(kref, struct aa_namespace, count);
+
+	free_aa_namespace(ns);
+}
+
+/**
+ * alloc_aa_namespace - allocate, initialize and return a new namespace
+ * @name: a preallocated name
+ * Returns NULL on failure.
+ */
+struct aa_namespace *alloc_aa_namespace(char *name)
+{
+	struct aa_namespace *ns;
+
+	ns = kzalloc(sizeof(*ns), GFP_KERNEL);
+	AA_DEBUG("%s(%p)\n", __FUNCTION__, ns);
+	if (ns) {
+		ns->name = name;
+		INIT_LIST_HEAD(&ns->list);
+		INIT_LIST_HEAD(&ns->profiles);
+		kref_init(&ns->count);
+		rwlock_init(&ns->lock);
+
+		ns->null_complain_profile = alloc_aa_profile();
+		if (!ns->null_complain_profile) {
+			if (!name)
+				kfree(ns->name);
+			kfree(ns);
+			return NULL;
+		}
+		ns->null_complain_profile->name =
+			kstrdup("null-complain-profile", GFP_KERNEL);
+		if (!ns->null_complain_profile->name) {
+			free_aa_profile(ns->null_complain_profile);
+			if (!name)
+				kfree(ns->name);
+			kfree(ns);
+			return NULL;
+		}
+		ns->null_complain_profile->flags.complain = 1;
+		/* null_complain_profile doesn't contribute to ns ref count */
+		ns->null_complain_profile->ns = ns;
+	}
+	return ns;
+}
+
+/**
+ * free_aa_namespace - free a profile namespace
+ * @namespace: the namespace to free
+ *
+ * Free a namespace.  All references to the namespace must have been put.
+ * If the namespace was referenced by a profile confining a task,
+ * free_aa_namespace will be called indirectly (through free_aa_profile)
+ * from an rcu callback routine, so we must not sleep here.
+ */
+void free_aa_namespace(struct aa_namespace *ns)
+{
+	AA_DEBUG("%s(%p)\n", __FUNCTION__, ns);
+
+	if (!ns)
+		return;
+
+	/* namespace still contains profiles -- invalid */
+	if (!list_empty(&ns->profiles)) {
+		AA_ERROR("%s: internal error, "
+			 "namespace '%s' still contains profiles\n",
+			 __FUNCTION__,
+			 ns->name);
+		BUG();
+	}
+	if (!list_empty(&ns->list)) {
+		AA_ERROR("%s: internal error, "
+			 "namespace '%s' still on list\n",
+			 __FUNCTION__,
+			 ns->name);
+		BUG();
+	}
+	/* null_complain_profile doesn't contribute to ns ref counting */
+	ns->null_complain_profile->ns = NULL;
+	aa_put_profile(ns->null_complain_profile);
+	kfree(ns->name);
+	kfree(ns);
+}
+
+/**
+ * free_aa_profile_kref - free aa_profile by kref (called by aa_put_profile)
+ * @kr: kref callback for freeing of a profile
+ */
+void free_aa_profile_kref(struct kref *kref)
+{
+	struct aa_profile *p=container_of(kref, struct aa_profile, count);
+
+	free_aa_profile(p);
+}
+
+/**
+ * alloc_aa_profile - allocate, initialize and return a new profile
+ * Returns NULL on failure.
+ */
+struct aa_profile *alloc_aa_profile(void)
+{
+	struct aa_profile *profile;
+
+	profile = kzalloc(sizeof(*profile), GFP_KERNEL);
+	AA_DEBUG("%s(%p)\n", __FUNCTION__, profile);
+	if (profile) {
+		INIT_LIST_HEAD(&profile->list);
+		kref_init(&profile->count);
+		INIT_LIST_HEAD(&profile->task_contexts);
+		spin_lock_init(&profile->lock);
+	}
+	return profile;
+}
+
+/**
+ * free_aa_profile - free a profile
+ * @profile: the profile to free
+ *
+ * Free a profile, its hats and null_profile. All references to the profile,
+ * its hats and null_profile must have been put.
+ *
+ * If the profile was referenced from a task context, free_aa_profile() will
+ * be called from an rcu callback routine, so we must not sleep here.
+ */
+void free_aa_profile(struct aa_profile *profile)
+{
+	AA_DEBUG("%s(%p)\n", __FUNCTION__, profile);
+
+	if (!profile)
+		return;
+
+	/* profile is still on profile namespace list -- invalid */
+	if (!list_empty(&profile->list)) {
+		AA_ERROR("%s: internal error, "
+			 "profile '%s' still on global list\n",
+			 __FUNCTION__,
+			 profile->name);
+		BUG();
+	}
+	aa_put_namespace(profile->ns);
+
+	aa_match_free(profile->file_rules);
+
+	if (profile->name) {
+		AA_DEBUG("%s: %s\n", __FUNCTION__, profile->name);
+		kfree(profile->name);
+	}
+
+	kfree(profile);
+}
+
+/**
+ * aa_unconfine_tasks - remove tasks on a profile's task context list
+ * @profile: profile to remove tasks from
+ *
+ * Assumes that @profile lock is held.
+ */
+void aa_unconfine_tasks(struct aa_profile *profile)
+{
+	while (!list_empty(&profile->task_contexts)) {
+		struct task_struct *task =
+			list_entry(profile->task_contexts.next,
+				   struct aa_task_context, list)->task;
+		task_lock(task);
+		aa_change_task_context(task, NULL, NULL, 0, NULL);
+		task_unlock(task);
+	}
+}

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 44/47] AppArmor: all the rest
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (42 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 43/47] AppArmor: Profile loading and manipulation, pathname matching John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 45/47] Add AppArmor LSM to security/Makefile John, Johansen
                   ` (2 subsequent siblings)
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, John Johansen,
	Andreas Gruenbacher

[-- Attachment #1: apparmor-misc.diff --]
[-- Type: text/plain, Size: 37045 bytes --]

All the things that didn't nicely fit in a category on their own: kbuild
code, declararions and inline functions, /sys/kernel/security/apparmor
filesystem for controlling apparmor from user space, profile list
functions, locking documentation, /proc/$pid/task/$tid/attr/current
access.

Signed-off-by: John Johansen <jjohansen@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>

---
 security/apparmor/Kconfig      |    9 +
 security/apparmor/Makefile     |   13 +
 security/apparmor/apparmor.h   |  323 +++++++++++++++++++++++++++++++++++++++++
 security/apparmor/apparmorfs.c |  252 +++++++++++++++++++++++++++++++
 security/apparmor/inline.h     |  240 ++++++++++++++++++++++++++++++
 security/apparmor/list.c       |  156 +++++++++++++++++++
 security/apparmor/locking.txt  |   68 ++++++++
 security/apparmor/procattr.c   |  194 ++++++++++++++++++++++++
 8 files changed, 1255 insertions(+)

--- /dev/null
+++ b/security/apparmor/Kconfig
@@ -0,0 +1,9 @@
+config SECURITY_APPARMOR
+	tristate "AppArmor support"
+	depends on SECURITY!=n
+	help
+	  This enables the AppArmor security module.
+	  Required userspace tools (if they are not included in your
+	  distribution) and further information may be found at
+	  <http://forge.novell.com/modules/xfmod/project/?apparmor>
+	  If you are unsure how to answer this question, answer N.
--- /dev/null
+++ b/security/apparmor/Makefile
@@ -0,0 +1,13 @@
+# Makefile for AppArmor Linux Security Module
+#
+obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
+
+apparmor-y := main.o list.o procattr.o lsm.o apparmorfs.o \
+	      module_interface.o match.o
+
+quiet_cmd_make-caps = GEN     $@
+cmd_make-caps = sed -n -e "/CAP_FS_MASK/d" -e "s/^\#define[ \\t]\\+CAP_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\$$/[\\2]  = \"\\1\",/p" $< | tr A-Z a-z > $@
+
+$(obj)/main.o : $(obj)/capability_names.h
+$(obj)/capability_names.h : $(srctree)/include/linux/capability.h
+	$(call cmd,make-caps)
--- /dev/null
+++ b/security/apparmor/apparmor.h
@@ -0,0 +1,323 @@
+/*
+ *	Copyright (C) 1998-2007 Novell/SUSE
+ *
+ *	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, version 2 of the
+ *	License.
+ *
+ *	AppArmor internal prototypes
+ */
+
+#ifndef __APPARMOR_H
+#define __APPARMOR_H
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/binfmts.h>
+#include <linux/rcupdate.h>
+
+/*
+ * We use MAY_READ, MAY_WRITE, MAY_EXEC, MAY_APPEND and the following flags
+ * for profile permissions
+ */
+#define AA_MAY_LINK			0x0010
+#define AA_MAY_LOCK			0x0020
+#define AA_EXEC_MMAP			0x0040
+#define AA_EXEC_UNSAFE			0x0080
+#define AA_EXEC_MOD_0			0x0100
+#define AA_EXEC_MOD_1			0x0200
+#define AA_BASE_PERMS			(MAY_READ | MAY_WRITE | MAY_EXEC | \
+					 MAY_APPEND | AA_MAY_LINK | \
+					 AA_MAY_LOCK | AA_EXEC_MMAP | \
+					 AA_EXEC_UNSAFE | AA_EXEC_MOD_0 | \
+					 AA_EXEC_MOD_1)
+#define AA_LINK_SUBSET_TEST		0x0020
+
+#define AA_EXEC_UNCONFINED		0
+#define AA_EXEC_INHERIT			AA_EXEC_MOD_0
+#define AA_EXEC_PROFILE			AA_EXEC_MOD_1
+#define AA_EXEC_PIX			(AA_EXEC_MOD_0 | AA_EXEC_MOD_1)
+
+#define AA_EXEC_MODIFIERS		(AA_EXEC_MOD_0 | AA_EXEC_MOD_1)
+
+#define AA_USER_SHIFT			0
+#define AA_OTHER_SHIFT			10
+
+#define AA_USER_PERMS			(AA_BASE_PERMS << AA_USER_SHIFT)
+#define AA_OTHER_PERMS			(AA_BASE_PERMS << AA_OTHER_SHIFT)
+
+#define AA_FILE_PERMS			(AA_USER_PERMS | AA_OTHER_PERMS)
+
+#define AA_LINK_BITS			((AA_MAY_LINK << AA_USER_SHIFT) | \
+					 (AA_MAY_LINK << AA_OTHER_SHIFT))
+
+#define AA_USER_EXEC			(MAY_EXEC << AA_USER_SHIFT)
+#define AA_OTHER_EXEC			(MAY_EXEC << AA_OTHER_SHIFT)
+
+#define AA_USER_EXEC_MODS		(AA_EXEC_MODIFIERS << AA_USER_SHIFT)
+#define AA_OTHER_EXEC_MODS		(AA_EXEC_MODIFIERS << AA_OTHER_SHIFT)
+
+#define AA_USER_EXEC_UNSAFE		(AA_EXEC_UNSAFE << AA_USER_SHIFT)
+#define AA_OTHER_EXEC_UNSAFE		(AA_EXEC_UNSAFE << AA_OTHER_SHIFT)
+
+#define AA_EXEC_BITS			(AA_USER_EXEC | AA_OTHER_EXEC)
+
+#define AA_ALL_EXEC_MODS		(AA_USER_EXEC_MODS | \
+					 AA_OTHER_EXEC_MODS)
+
+/* shared permissions that are not duplicated in user:group:other */
+#define AA_CHANGE_PROFILE		0x40000000
+
+#define AA_SHARED_PERMS			(AA_CHANGE_PROFILE)
+
+#define AA_VALID_PERM_MASK		(AA_FILE_PERMS | AA_SHARED_PERMS)
+
+#define AA_SECURE_EXEC_NEEDED		1
+
+/* Control parameters (0 or 1), settable thru module/boot flags or
+ * via /sys/kernel/security/apparmor/control */
+extern int apparmor_complain;
+extern int apparmor_debug;
+extern int apparmor_audit;
+extern int apparmor_logsyscall;
+extern unsigned int apparmor_path_max;
+
+#define PROFILE_COMPLAIN(_profile) \
+	(apparmor_complain == 1 || ((_profile) && (_profile)->flags.complain))
+
+#define APPARMOR_COMPLAIN(_cxt) \
+	(apparmor_complain == 1 || \
+	 ((_cxt) && (_cxt)->profile && (_cxt)->profile->flags.complain))
+
+#define PROFILE_AUDIT(_profile) \
+	(apparmor_audit == 1 || ((_profile) && (_profile)->flags.audit))
+
+#define APPARMOR_AUDIT(_cxt) \
+	(apparmor_audit == 1 || \
+	 ((_cxt) && (_cxt)->profile && (_cxt)->profile->flags.audit))
+
+/*
+ * DEBUG remains global (no per profile flag) since it is mostly used in sysctl
+ * which is not related to profile accesses.
+ */
+
+#define AA_DEBUG(fmt, args...)						\
+	do {								\
+		if (apparmor_debug)					\
+			printk(KERN_DEBUG "AppArmor: " fmt, ##args);	\
+	} while (0)
+
+#define AA_ERROR(fmt, args...)	printk(KERN_ERR "AppArmor: " fmt, ##args)
+
+struct aa_profile;
+
+/* struct aa_namespace - namespace for a set of profiles
+ * @name: the name of the namespace
+ * @list: list the namespace is on
+ * @profiles: list of profile in the namespace
+ * @profile_count: the number of profiles in the namespace
+ * @null_complain_profile: special profile used for learning in this namespace
+ * @count: reference count on the namespace
+ * @lock: lock for adding/removing profile to the namespace
+ */
+struct aa_namespace {
+	char *name;
+	struct list_head list;
+	struct list_head profiles;
+	int profile_count;
+	struct aa_profile *null_complain_profile;
+
+	struct kref count;
+	rwlock_t lock;
+};
+
+/* struct aa_profile - basic confinement data
+ * @name: the profiles name
+ * @list: list this profile is on
+ * @ns: namespace the profile is in
+ * @file_rules: dfa containing the profiles file rules
+ * @flags: flags controlling profile behavior
+ * @isstale: flag indicating if profile is stale
+ * @capabilities: capabilities granted by the process
+ * @count: reference count of the profile
+ *
+ * The AppArmor profile contains the basic confinement data.  Each profile
+ * has a name, and all nonstale profile are in a profile namespace.
+ *
+ * The task_contexts list and the isstale flag are protected by the
+ * profile lock.
+ *
+ * If a task context is moved between two profiles, we first need to grab
+ * both profile locks. lock_both_profiles() does that in a deadlock-safe
+ * way.
+ */
+struct aa_profile {
+	char *name;
+	struct list_head list;
+	struct aa_namespace *ns;
+
+	struct aa_dfa *file_rules;
+	struct {
+		int complain;
+		int audit;
+	} flags;
+	int isstale;
+
+	kernel_cap_t capabilities;
+	struct kref count;
+	struct list_head task_contexts;
+	spinlock_t lock;
+	unsigned long int_flags;
+};
+
+extern struct list_head profile_ns_list;
+extern rwlock_t profile_ns_list_lock;
+extern struct mutex aa_interface_lock;
+
+/**
+ * struct aa_task_context - primary label for confined tasks
+ * @profile: the current profile
+ * @previous_profile: profile the task may return to
+ * @cookie: magic value the task must know for returning to @previous_profile
+ * @list: list this aa_task_context is on
+ * @task: task that the aa_task_context confines
+ * @rcu: rcu head used when freeing the aa_task_context
+ * @caps_logged: caps that have previously generated log entries
+ *
+ * Contains the task's current profile (which could change due to
+ * change_hat).  Plus the hat_magic needed during change_hat.
+ */
+struct aa_task_context {
+	struct aa_profile *profile;
+	struct aa_profile *previous_profile;
+	u64 cookie;
+	struct list_head list;
+	struct task_struct *task;
+	struct rcu_head rcu;
+	kernel_cap_t caps_logged;
+};
+
+extern struct aa_namespace *default_namespace;
+
+/* aa_audit - AppArmor auditing structure
+ * Structure is populated by access control code and passed to aa_audit which
+ * provides for a single point of logging.
+ */
+
+struct aa_audit {
+	const char *operation;
+	gfp_t gfp_mask;
+	const char *info;
+	const char *name;
+	const char *name2;
+	const char *name3;
+	int request_mask, denied_mask;
+	struct iattr *iattr;
+	pid_t task, parent;
+	int error_code;
+};
+
+/* Flags for the permission check functions */
+#define AA_CHECK_FD	1  /* coming from a file descriptor */
+#define AA_CHECK_DIR	2  /* file type is directory */
+
+/* lock subtypes so lockdep does not raise false dependencies */
+enum aa_lock_class {
+	aa_lock_normal,
+	aa_lock_nested,
+	aa_lock_task_release
+};
+
+/* main.c */
+extern int alloc_default_namespace(void);
+extern void free_default_namespace(void);
+extern int aa_audit_message(struct aa_profile *profile, struct aa_audit *sa,
+			    int type);
+void aa_audit_hint(struct aa_profile *profile, struct aa_audit *sa);
+void aa_audit_status(struct aa_profile *profile, struct aa_audit *sa);
+int aa_audit_reject(struct aa_profile *profile, struct aa_audit *sa);
+extern int aa_audit_syscallreject(struct aa_profile *profile, gfp_t gfp,
+				  const char *);
+extern int aa_audit(struct aa_profile *profile, struct aa_audit *);
+
+extern int aa_attr(struct aa_profile *profile, struct dentry *dentry,
+		   struct vfsmount *mnt, struct iattr *iattr);
+extern int aa_perm_xattr(struct aa_profile *profile, const char *operation,
+			 struct dentry *dentry, struct vfsmount *mnt,
+			 int mask, int check);
+extern int aa_capability(struct aa_task_context *cxt, int cap);
+extern int aa_perm(struct aa_profile *profile, const char *operation,
+		   struct dentry *dentry, struct vfsmount *mnt, int mask,
+		   int check);
+extern int aa_perm_dir(struct aa_profile *profile, const char *operation,
+		       struct dentry *dentry, struct vfsmount *mnt,
+		       int mask);
+extern int aa_perm_path(struct aa_profile *, const char *operation,
+			const char *name, int mask, uid_t uid);
+extern int aa_link(struct aa_profile *profile,
+		   struct dentry *link, struct vfsmount *link_mnt,
+		   struct dentry *target, struct vfsmount *target_mnt);
+extern int aa_clone(struct task_struct *task);
+extern int aa_register(struct linux_binprm *bprm);
+extern void aa_release(struct task_struct *task);
+extern int aa_change_hat(const char *id, u64 hat_magic);
+extern int aa_change_profile(const char *ns_name, const char *name);
+extern struct aa_profile *__aa_replace_profile(struct task_struct *task,
+					       struct aa_profile *profile);
+extern struct aa_task_context *lock_task_and_profiles(struct task_struct *task,
+						      struct aa_profile *profile);
+extern void unlock_task_and_profiles(struct task_struct *task,
+				     struct aa_task_context *cxt,
+				     struct aa_profile *profile);
+extern void aa_change_task_context(struct task_struct *task,
+				   struct aa_task_context *new_cxt,
+				   struct aa_profile *profile, u64 cookie,
+				   struct aa_profile *previous_profile);
+extern int aa_may_ptrace(struct aa_task_context *cxt,
+			 struct aa_profile *tracee);
+
+/* list.c */
+extern struct aa_namespace *__aa_find_namespace(const char *name,
+						struct list_head *list);
+extern struct aa_profile *__aa_find_profile(const char *name,
+					    struct list_head *list);
+extern void aa_profile_ns_list_release(void);
+
+/* module_interface.c */
+extern ssize_t aa_add_profile(void *, size_t);
+extern ssize_t aa_replace_profile(void *, size_t);
+extern ssize_t aa_remove_profile(char *, size_t);
+extern struct aa_namespace *alloc_aa_namespace(char *name);
+extern void free_aa_namespace(struct aa_namespace *ns);
+extern void free_aa_namespace_kref(struct kref *kref);
+extern struct aa_profile *alloc_aa_profile(void);
+extern void free_aa_profile(struct aa_profile *profile);
+extern void free_aa_profile_kref(struct kref *kref);
+extern void aa_unconfine_tasks(struct aa_profile *profile);
+
+/* procattr.c */
+extern int aa_getprocattr(struct aa_profile *profile, char **string,
+			  unsigned *len);
+extern int aa_setprocattr_changehat(char *args);
+extern int aa_setprocattr_changeprofile(char *args);
+extern int aa_setprocattr_setprofile(struct task_struct *task, char *args);
+
+/* apparmorfs.c */
+extern int create_apparmorfs(void);
+extern void destroy_apparmorfs(void);
+
+/* match.c */
+extern struct aa_dfa *aa_match_alloc(void);
+extern void aa_match_free(struct aa_dfa *dfa);
+extern int unpack_dfa(struct aa_dfa *dfa, void *blob, size_t size);
+extern int verify_dfa(struct aa_dfa *dfa);
+extern unsigned int aa_dfa_match(struct aa_dfa *dfa, const char *str);
+extern unsigned int aa_dfa_next_state(struct aa_dfa *dfa, unsigned int start,
+				      const char *str);
+extern unsigned int aa_match_state(struct aa_dfa *dfa, unsigned int start,
+				   const char *str, unsigned int *final);
+extern unsigned int aa_dfa_null_transition(struct aa_dfa *dfa,
+					   unsigned int start);
+
+#endif  /* __APPARMOR_H */
--- /dev/null
+++ b/security/apparmor/apparmorfs.c
@@ -0,0 +1,252 @@
+/*
+ *	Copyright (C) 1998-2007 Novell/SUSE
+ *
+ *	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, version 2 of the
+ *	License.
+ *
+ *	AppArmor filesystem (part of securityfs)
+ */
+
+#include <linux/security.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <asm/uaccess.h>
+
+#include "apparmor.h"
+#include "inline.h"
+
+static char *aa_simple_write_to_buffer(const char __user *userbuf,
+				       size_t alloc_size, size_t copy_size,
+				       loff_t *pos, const char *operation)
+{
+	struct aa_profile *profile;
+	char *data;
+
+	if (*pos != 0) {
+		/* only writes from pos 0, that is complete writes */
+		data = ERR_PTR(-ESPIPE);
+		goto out;
+	}
+
+	/*
+	 * Don't allow confined processes to load/replace/remove profiles.
+	 * No sane person would add rules allowing this to a profile
+	 * but we enforce the restriction anyways.
+	 */
+	profile = aa_get_profile(current);
+	if (profile) {
+		struct aa_audit sa;
+		memset(&sa, 0, sizeof(sa));
+		sa.operation = operation;
+		sa.gfp_mask = GFP_KERNEL;
+		sa.error_code = -EACCES;
+		data = ERR_PTR(aa_audit_reject(profile, &sa));
+		aa_put_profile(profile);
+		goto out;
+	}
+
+	data = vmalloc(alloc_size);
+	if (data == NULL) {
+		data = ERR_PTR(-ENOMEM);
+		goto out;
+	}
+
+	if (copy_from_user(data, userbuf, copy_size)) {
+		vfree(data);
+		data = ERR_PTR(-EFAULT);
+		goto out;
+	}
+
+out:
+	return data;
+}
+
+/* apparmor/profiles */
+extern struct seq_operations apparmorfs_profiles_op;
+
+static int aa_profiles_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &apparmorfs_profiles_op);
+}
+
+
+static int aa_profiles_release(struct inode *inode, struct file *file)
+{
+	return seq_release(inode, file);
+}
+
+static struct file_operations apparmorfs_profiles_fops = {
+	.open =		aa_profiles_open,
+	.read =		seq_read,
+	.llseek =	seq_lseek,
+	.release =	aa_profiles_release,
+};
+
+/* apparmor/matching */
+static ssize_t aa_matching_read(struct file *file, char __user *buf,
+			       size_t size, loff_t *ppos)
+{
+	const char *matching = "pattern=aadfa perms=rwxamlz user:other";
+
+	return simple_read_from_buffer(buf, size, ppos, matching,
+				       strlen(matching));
+}
+
+static struct file_operations apparmorfs_matching_fops = {
+	.read = 	aa_matching_read,
+};
+
+/* apparmor/.load */
+static ssize_t aa_profile_load(struct file *f, const char __user *buf,
+			       size_t size, loff_t *pos)
+{
+	char *data;
+	ssize_t error;
+
+	data = aa_simple_write_to_buffer(buf, size, size, pos, "profile_load");
+
+	error = PTR_ERR(data);
+	if (!IS_ERR(data)) {
+		error = aa_add_profile(data, size);
+		vfree(data);
+	}
+
+	return error;
+}
+
+
+static struct file_operations apparmorfs_profile_load = {
+	.write = aa_profile_load
+};
+
+/* apparmor/.replace */
+static ssize_t aa_profile_replace(struct file *f, const char __user *buf,
+				  size_t size, loff_t *pos)
+{
+	char *data;
+	ssize_t error;
+
+	data = aa_simple_write_to_buffer(buf, size, size, pos,
+					 "profile_replace");
+
+	error = PTR_ERR(data);
+	if (!IS_ERR(data)) {
+		error = aa_replace_profile(data, size);
+		vfree(data);
+	}
+
+	return error;
+}
+
+
+static struct file_operations apparmorfs_profile_replace = {
+	.write = aa_profile_replace
+};
+
+/* apparmor/.remove */
+static ssize_t aa_profile_remove(struct file *f, const char __user *buf,
+				  size_t size, loff_t *pos)
+{
+	char *data;
+	ssize_t error;
+
+	/*
+	 * aa_remove_profile needs a null terminated string so 1 extra
+	 * byte is allocated and the copied data is null terminated.
+	 */
+	data = aa_simple_write_to_buffer(buf, size + 1, size, pos,
+					 "profile_remove");
+
+	error = PTR_ERR(data);
+	if (!IS_ERR(data)) {
+		data[size] = 0;
+		error = aa_remove_profile(data, size);
+		vfree(data);
+	}
+
+	return error;
+}
+
+static struct file_operations apparmorfs_profile_remove = {
+	.write = aa_profile_remove
+};
+
+static struct dentry *apparmor_dentry;
+
+static void aafs_remove(const char *name)
+{
+	struct dentry *dentry;
+
+	dentry = lookup_one_len(name, apparmor_dentry, strlen(name));
+	if (!IS_ERR(dentry)) {
+		securityfs_remove(dentry);
+		dput(dentry);
+	}
+}
+
+static int aafs_create(const char *name, int mask, struct file_operations *fops)
+{
+	struct dentry *dentry;
+
+	dentry = securityfs_create_file(name, S_IFREG | mask, apparmor_dentry,
+					NULL, fops);
+
+	return IS_ERR(dentry) ? PTR_ERR(dentry) : 0;
+}
+
+void destroy_apparmorfs(void)
+{
+	if (apparmor_dentry) {
+		aafs_remove(".remove");
+		aafs_remove(".replace");
+		aafs_remove(".load");
+		aafs_remove("matching");
+		aafs_remove("profiles");
+		securityfs_remove(apparmor_dentry);
+		apparmor_dentry = NULL;
+	}
+}
+
+int create_apparmorfs(void)
+{
+	int error;
+
+	if (apparmor_dentry) {
+		AA_ERROR("%s: AppArmor securityfs already exists\n",
+			__FUNCTION__);
+		return -EEXIST;
+	}
+
+	apparmor_dentry = securityfs_create_dir("apparmor", NULL);
+	if (IS_ERR(apparmor_dentry)) {
+		error = PTR_ERR(apparmor_dentry);
+		apparmor_dentry = NULL;
+ 		goto error;
+	}
+	error = aafs_create("profiles", 0440, &apparmorfs_profiles_fops);
+	if (error)
+		goto error;
+	error = aafs_create("matching", 0444, &apparmorfs_matching_fops);
+	if (error)
+		goto error;
+	error = aafs_create(".load", 0640, &apparmorfs_profile_load);
+	if (error)
+		goto error;
+	error = aafs_create(".replace", 0640, &apparmorfs_profile_replace);
+	if (error)
+		goto error;
+	error = aafs_create(".remove", 0640, &apparmorfs_profile_remove);
+	if (error)
+		goto error;
+
+	return 0;
+
+error:
+	destroy_apparmorfs();
+	AA_ERROR("Error creating AppArmor securityfs\n");
+	return error;
+}
+
--- /dev/null
+++ b/security/apparmor/inline.h
@@ -0,0 +1,240 @@
+/*
+ *	Copyright (C) 1998-2007 Novell/SUSE
+ *
+ *	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, version 2 of the
+ *	License.
+ */
+
+#ifndef __INLINE_H
+#define __INLINE_H
+
+#include <linux/sched.h>
+
+#include "match.h"
+
+static inline int mediated_filesystem(struct inode *inode)
+{
+	return !(inode->i_sb->s_flags & MS_NOUSER);
+}
+
+static inline struct aa_task_context *aa_task_context(struct task_struct *task)
+{
+	return (struct aa_task_context *) rcu_dereference(task->security);
+}
+
+static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns)
+{
+	if (ns)
+		kref_get(&(ns->count));
+
+	return ns;
+}
+
+static inline void aa_put_namespace(struct aa_namespace *ns)
+{
+	if (ns)
+		kref_put(&ns->count, free_aa_namespace_kref);
+}
+
+
+static inline struct aa_namespace *aa_find_namespace(const char *name)
+{
+	struct aa_namespace *ns = NULL;
+
+	read_lock(&profile_ns_list_lock);
+	ns = aa_get_namespace(__aa_find_namespace(name, &profile_ns_list));
+	read_unlock(&profile_ns_list_lock);
+
+	return ns;
+}
+
+/**
+ * aa_dup_profile - increment refcount on profile @p
+ * @p: profile
+ */
+static inline struct aa_profile *aa_dup_profile(struct aa_profile *p)
+{
+	if (p)
+		kref_get(&(p->count));
+
+	return p;
+}
+
+/**
+ * aa_put_profile - decrement refcount on profile @p
+ * @p: profile
+ */
+static inline void aa_put_profile(struct aa_profile *p)
+{
+	if (p)
+		kref_put(&p->count, free_aa_profile_kref);
+}
+
+static inline struct aa_profile *aa_get_profile(struct task_struct *task)
+{
+	struct aa_task_context *cxt;
+	struct aa_profile *profile = NULL;
+
+	rcu_read_lock();
+	cxt = aa_task_context(task);
+	if (cxt) {
+		profile = cxt->profile;
+		aa_dup_profile(profile);
+	}
+	rcu_read_unlock();
+
+	return profile;
+}
+
+static inline struct aa_profile *aa_find_profile(struct aa_namespace *ns,
+						 const char *name)
+{
+	struct aa_profile *profile = NULL;
+
+	read_lock(&ns->lock);
+	profile = aa_dup_profile(__aa_find_profile(name, &ns->profiles));
+	read_unlock(&ns->lock);
+
+	return profile;
+}
+
+static inline struct aa_task_context *aa_alloc_task_context(gfp_t flags)
+{
+	struct aa_task_context *cxt;
+
+	cxt = kzalloc(sizeof(*cxt), flags);
+	if (cxt) {
+		INIT_LIST_HEAD(&cxt->list);
+		INIT_RCU_HEAD(&cxt->rcu);
+	}
+
+	return cxt;
+}
+
+static inline void aa_free_task_context(struct aa_task_context *cxt)
+{
+	if (cxt) {
+		aa_put_profile(cxt->profile);
+		aa_put_profile(cxt->previous_profile);
+		kfree(cxt);
+	}
+}
+
+/**
+ * lock_profile - lock a profile
+ * @profile: the profile to lock
+ *
+ * While the profile is locked, local interrupts are disabled. This also
+ * gives us RCU reader safety.
+ */
+static inline void lock_profile_nested(struct aa_profile *profile,
+				       enum aa_lock_class lock_class)
+{
+	/*
+	 * Lock the profile.
+	 *
+	 * Need to disable interrupts here because this lock is used in
+	 * the task_free_security hook, which may run in RCU context.
+	 */
+	if (profile)
+		spin_lock_irqsave_nested(&profile->lock, profile->int_flags,
+					 lock_class);
+}
+
+static inline void lock_profile(struct aa_profile *profile)
+{
+	lock_profile_nested(profile, aa_lock_normal);
+}
+
+/**
+ * unlock_profile - unlock a profile
+ * @profile: the profile to unlock
+ */
+static inline void unlock_profile(struct aa_profile *profile)
+{
+	/* Unlock the profile. */
+	if (profile)
+		spin_unlock_irqrestore(&profile->lock, profile->int_flags);
+}
+
+/**
+ * lock_both_profiles  -  lock two profiles in a deadlock-free way
+ * @profile1:	profile to lock (may be NULL)
+ * @profile2:	profile to lock (may be NULL)
+ *
+ * The order in which profiles are passed into lock_both_profiles() /
+ * unlock_both_profiles() does not matter.
+ * While the profile is locked, local interrupts are disabled. This also
+ * gives us RCU reader safety.
+ */
+static inline void lock_both_profiles(struct aa_profile *profile1,
+				      struct aa_profile *profile2)
+{
+	/*
+	 * Lock the two profiles.
+	 *
+	 * We need to disable interrupts because the profile locks are
+	 * used in the task_free_security hook, which may run in RCU
+	 * context.
+	 *
+	 * Do not nest spin_lock_irqsave()/spin_unlock_irqresore():
+	 * interrupts only need to be turned off once.
+	 */
+	if (!profile1 || profile1 == profile2) {
+		if (profile2)
+			spin_lock_irqsave_nested(&profile2->lock,
+						 profile2->int_flags,
+						 aa_lock_normal);
+	} else if (profile1 > profile2) {
+		/* profile1 cannot be NULL here. */
+		spin_lock_irqsave_nested(&profile1->lock, profile1->int_flags,
+					 aa_lock_normal);
+		if (profile2)
+			spin_lock_nested(&profile2->lock, aa_lock_nested);
+
+	} else {
+		/* profile2 cannot be NULL here. */
+		spin_lock_irqsave_nested(&profile2->lock, profile2->int_flags,
+					 aa_lock_normal);
+		spin_lock_nested(&profile1->lock, aa_lock_nested);
+	}
+}
+
+/**
+ * unlock_both_profiles  -  unlock two profiles in a deadlock-free way
+ * @profile1:	profile to unlock (may be NULL)
+ * @profile2:	profile to unlock (may be NULL)
+ *
+ * The order in which profiles are passed into lock_both_profiles() /
+ * unlock_both_profiles() does not matter.
+ * While the profile is locked, local interrupts are disabled. This also
+ * gives us RCU reader safety.
+ */
+static inline void unlock_both_profiles(struct aa_profile *profile1,
+				        struct aa_profile *profile2)
+{
+	/* Unlock the two profiles. */
+	if (!profile1 || profile1 == profile2) {
+		if (profile2)
+			spin_unlock_irqrestore(&profile2->lock,
+					       profile2->int_flags);
+	} else if (profile1 > profile2) {
+		/* profile1 cannot be NULL here. */
+		if (profile2)
+			spin_unlock(&profile2->lock);
+		spin_unlock_irqrestore(&profile1->lock, profile1->int_flags);
+	} else {
+		/* profile2 cannot be NULL here. */
+		spin_unlock(&profile1->lock);
+		spin_unlock_irqrestore(&profile2->lock, profile2->int_flags);
+	}
+}
+
+static inline unsigned int aa_match(struct aa_dfa *dfa, const char *pathname)
+{
+	        return dfa ? aa_dfa_match(dfa, pathname) : 0;
+}
+
+#endif /* __INLINE_H__ */
--- /dev/null
+++ b/security/apparmor/list.c
@@ -0,0 +1,156 @@
+/*
+ *	Copyright (C) 1998-2007 Novell/SUSE
+ *
+ *	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, version 2 of the
+ *	License.
+ *
+ *	AppArmor Profile List Management
+ */
+
+#include <linux/seq_file.h>
+#include "apparmor.h"
+#include "inline.h"
+
+/* list of profile namespaces and lock */
+LIST_HEAD(profile_ns_list);
+rwlock_t profile_ns_list_lock = RW_LOCK_UNLOCKED;
+
+/**
+ * __aa_find_namespace  -  look up a profile namespace on the namespace list
+ * @name: name of namespace to find
+ * @head: list to search
+ *
+ * Returns a pointer to the namespace on the list, or NULL if no namespace
+ * called @name exists. The caller must hold the profile_ns_list_lock.
+ */
+struct aa_namespace *__aa_find_namespace(const char *name,
+				       struct list_head *head)
+{
+	struct aa_namespace *ns;
+
+	list_for_each_entry(ns, head, list) {
+		if (!strcmp(ns->name, name))
+			return ns;
+	}
+
+	return NULL;
+}
+
+/**
+ * __aa_find_profile  -  look up a profile on the profile list
+ * @name: name of profile to find
+ * @head: list to search
+ *
+ * Returns a pointer to the profile on the list, or NULL if no profile
+ * called @name exists. The caller must hold the profile_list_lock.
+ */
+struct aa_profile *__aa_find_profile(const char *name, struct list_head *head)
+{
+	struct aa_profile *profile;
+
+	list_for_each_entry(profile, head, list) {
+		if (!strcmp(profile->name, name))
+			return profile;
+	}
+
+	return NULL;
+}
+
+static void aa_profile_list_release(struct list_head *head)
+{
+	struct aa_profile *profile, *tmp;
+	list_for_each_entry_safe(profile, tmp, head, list) {
+		/* Remove the profile from each task context it is on. */
+		lock_profile(profile);
+		profile->isstale = 1;
+		aa_unconfine_tasks(profile);
+		list_del_init(&profile->list);
+		unlock_profile(profile);
+		aa_put_profile(profile);
+	}
+}
+
+/**
+ * aa_profilelist_release - Remove all profiles from profile_list
+ */
+void aa_profile_ns_list_release(void)
+{
+	struct aa_namespace *ns, *tmp;
+
+	/* Remove and release all the profiles on namespace profile lists. */
+	write_lock(&profile_ns_list_lock);
+	list_for_each_entry_safe(ns, tmp, &profile_ns_list, list) {
+		write_lock(&ns->lock);
+		aa_profile_list_release(&ns->profiles);
+		list_del_init(&ns->list);
+		write_unlock(&ns->lock);
+		aa_put_namespace(ns);
+	}
+	write_unlock(&profile_ns_list_lock);
+}
+
+static void *p_start(struct seq_file *f, loff_t *pos)
+{
+	struct aa_namespace *ns;
+	struct aa_profile *profile;
+	loff_t l = *pos;
+	read_lock(&profile_ns_list_lock);
+	if (l--)
+		return NULL;
+	list_for_each_entry(ns, &profile_ns_list, list) {
+		read_lock(&ns->lock);
+		list_for_each_entry(profile, &ns->profiles, list)
+			return profile;
+		read_unlock(&ns->lock);
+	}
+	return NULL;
+}
+
+static void *p_next(struct seq_file *f, void *p, loff_t *pos)
+{
+	struct aa_profile *profile = (struct aa_profile *) p;
+	struct list_head *lh = profile->list.next;
+	struct aa_namespace *ns;
+	(*pos)++;
+	if (lh != &profile->ns->profiles)
+		return list_entry(lh, struct aa_profile, list);
+
+	lh = profile->ns->list.next;
+	read_unlock(&profile->ns->lock);
+	while (lh != &profile_ns_list) {
+		ns = list_entry(lh, struct aa_namespace, list);
+		read_lock(&ns->lock);
+		list_for_each_entry(profile, &ns->profiles, list)
+			return profile;
+		read_unlock(&ns->lock);
+		lh = ns->list.next;
+	}
+	return NULL;
+}
+
+static void p_stop(struct seq_file *f, void *v)
+{
+	read_unlock(&profile_ns_list_lock);
+}
+
+static int seq_show_profile(struct seq_file *f, void *v)
+{
+	struct aa_profile *profile = (struct aa_profile *)v;
+	if (profile->ns == default_namespace)
+	    seq_printf(f, "%s (%s)\n", profile->name,
+		       PROFILE_COMPLAIN(profile) ? "complain" : "enforce");
+	else
+	    seq_printf(f, "%s:%s (%s)\n", profile->ns->name, profile->name,
+		       PROFILE_COMPLAIN(profile) ? "complain" : "enforce");
+	return 0;
+}
+
+/* Used in apparmorfs.c */
+struct seq_operations apparmorfs_profiles_op = {
+	.start =	p_start,
+	.next =		p_next,
+	.stop =		p_stop,
+	.show =		seq_show_profile,
+};
--- /dev/null
+++ b/security/apparmor/locking.txt
@@ -0,0 +1,68 @@
+Locking in AppArmor
+===================
+
+Lock hierarchy:
+
+	aa_interface_lock
+	  profile_list_lock
+	    aa_profile->lock
+	      task_lock()
+
+
+Which lock protects what?
+
+	/-----------------------+-------------------------------\
+	| Variable		| Lock				|
+	>-----------------------+-------------------------------<
+	| profile_list		| profile_list_lock		|
+	+-----------------------+-------------------------------+
+	| aa_profile		| (reference count)		|
+	+-----------------------+-------------------------------+
+	| aa_profile->		| aa_profile->lock		|
+	|   isstale,		|				|
+	|   task_contexts	|				|
+	+-----------------------+-------------------------------+
+	| task_struct->security	| read: RCU			|
+	|			| write: task_lock()		|
+	+-----------------------+-------------------------------+
+	| aa_profile->sub	| handle on the profile (list	|
+	|			| is never modified)		|
+	\-----------------------+-------------------------------/
+
+(Obviously, the list_heads embedded in data structures are always
+protected with the lock that also protects the list.)
+
+When moving a task context from one profile to another, we grab both
+profile locks with lock_both_profiles(). This ensures that both locks
+are always taken in the same order, and so we won't deadlock.
+
+Since task_struct->security is RCU protected the aa_task_struct it
+references is only guarenteed to exist for the rcu cycle.  Where
+aa_task_context->profile is needed in blocking operations the
+profile's reference count is incremented and the profile reference
+is used.
+
+Profiles on profile_list are never stale: when a profile becomes stale,
+it is removed from profile_list at the same time (under profile_list_lock
+and aa_profile->lock).
+
+The aa_interface_lock is taken whenever user-space modifies the profile
+list, and can sleep. This ensures that profile loading/replacement/removal
+won't race with itself. We release the profile_list_lock as soon as
+possible to avoid stalling exec during profile loading/replacement/removal.
+
+AppArmor uses lock subtyping to avoid false positives from lockdep.  The
+profile lock is often taken nested, but it is guaranteed to be in a lock
+safe order and not the same lock when done, so it is safe.
+
+A third lock type (aa_lock_task_release) is given to the profile lock
+when it is taken in soft irq context during task release (aa_release).
+This is to avoid a false positive between the task lock and the profile
+lock.  In task context the profile lock wraps the task lock with irqs
+off, but the kernel takes the task lock with irqs enabled.  This won't
+result in a deadlock because for a deadlock to occur the kernel must
+take dead task A's lock (irqs on), the rcu callback hook freeing
+dead task A must be run and AppArmor must be changing the profile on
+dead task A.  The kernel should not be taking a dead task's task_lock
+at the same time the task is being freed by task rcu cleanup other wise
+the task would not be out of its quiescent period.
--- /dev/null
+++ b/security/apparmor/procattr.c
@@ -0,0 +1,194 @@
+/*
+ *	Copyright (C) 1998-2007 Novell/SUSE
+ *
+ *	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, version 2 of the
+ *	License.
+ *
+ *	AppArmor /proc/pid/attr handling
+ */
+
+#include "apparmor.h"
+#include "inline.h"
+
+int aa_getprocattr(struct aa_profile *profile, char **string, unsigned *len)
+{
+	char *str;
+
+	if (profile) {
+		const char *mode_str = PROFILE_COMPLAIN(profile) ?
+			" (complain)" : " (enforce)";
+		int mode_len, name_len, ns_len = 0;
+
+		mode_len = strlen(mode_str);
+		name_len = strlen(profile->name);
+		if (profile->ns != default_namespace)
+			ns_len = strlen(profile->ns->name) + 1;
+		*len = mode_len + ns_len + name_len + 1;
+		str = kmalloc(*len, GFP_ATOMIC);
+		if (!str)
+			return -ENOMEM;
+
+		if (ns_len) {
+			memcpy(str, profile->ns->name, ns_len - 1);
+			str += ns_len - 1;
+			*str++ = ':';
+		}
+		memcpy(str, profile->name, name_len);
+		str += name_len;
+		memcpy(str, mode_str, mode_len);
+		str += mode_len;
+		*str++ = '\n';
+		str -= *len;
+	} else {
+		const char *unconfined_str = "unconfined\n";
+
+		*len = strlen(unconfined_str);
+		str = kmalloc(*len, GFP_ATOMIC);
+		if (!str)
+			return -ENOMEM;
+
+		memcpy(str, unconfined_str, *len);
+	}
+	*string = str;
+
+	return 0;
+}
+
+static char *split_token_from_name(const char *op, char *args, u64 *cookie)
+{
+	char *name;
+
+	*cookie = simple_strtoull(args, &name, 16);
+	if ((name == args) || *name != '^') {
+		AA_ERROR("%s: Invalid input '%s'", op, args);
+		return ERR_PTR(-EINVAL);
+	}
+
+	name++;  /* skip ^ */
+	if (!*name)
+		name = NULL;
+	return name;
+}
+
+int aa_setprocattr_changehat(char *args)
+{
+	char *hat;
+	u64 cookie;
+
+	hat = split_token_from_name("change_hat", args, &cookie);
+	if (IS_ERR(hat))
+		return PTR_ERR(hat);
+
+	if (!hat && !cookie) {
+		AA_ERROR("change_hat: Invalid input, NULL hat and NULL magic");
+		return -EINVAL;
+	}
+
+	AA_DEBUG("%s: Magic 0x%llx Hat '%s'\n",
+		 __FUNCTION__, cookie, hat ? hat : NULL);
+
+	return aa_change_hat(hat, cookie);
+}
+
+int aa_setprocattr_changeprofile(char *args)
+{
+	char *name = args, *ns_name = NULL;
+
+	if (name[0] != '/') {
+		char *split = strchr(name, ':');
+		if (split) {
+			*split = 0;
+			ns_name = name;
+			name = split + 1;
+		}
+	}
+
+	return aa_change_profile(ns_name, name);
+}
+
+int aa_setprocattr_setprofile(struct task_struct *task, char *args)
+{
+	struct aa_profile *old_profile, *new_profile;
+	struct aa_namespace *ns;
+	struct aa_audit sa;
+	char *name, *ns_name = NULL;
+
+	memset(&sa, 0, sizeof(sa));
+	sa.operation = "profile_set";
+	sa.gfp_mask = GFP_KERNEL;
+	sa.task = task->pid;
+
+	AA_DEBUG("%s: current %d\n",
+		 __FUNCTION__, current->pid);
+
+	name = args;
+	if (args[0] != '/') {
+		char *split = strchr(args, ':');
+		if (split) {
+			*split = 0;
+			ns_name = args;
+			name = split + 1;
+		}
+	}
+	if (ns_name)
+		ns = aa_find_namespace(ns_name);
+	else
+		ns = aa_get_namespace(default_namespace);
+	if (!ns) {
+		sa.name = ns_name;
+		sa.info = "unknown namespace";
+		aa_audit_reject(NULL, &sa);
+		aa_put_namespace(ns);
+		return -EINVAL;
+	}
+
+repeat:
+	if (strcmp(name, "unconfined") == 0)
+		new_profile = NULL;
+	else {
+		new_profile = aa_find_profile(ns, name);
+		if (!new_profile) {
+			sa.name = ns_name;
+			sa.name2 = name;
+			sa.info = "unknown profile";
+			aa_audit_reject(NULL, &sa);
+			aa_put_namespace(ns);
+			return -EINVAL;
+		}
+	}
+
+	old_profile = __aa_replace_profile(task, new_profile);
+	if (IS_ERR(old_profile)) {
+		int error;
+
+		aa_put_profile(new_profile);
+		error = PTR_ERR(old_profile);
+		if (error == -ESTALE)
+			goto repeat;
+		aa_put_namespace(ns);
+		return error;
+	}
+
+	if (new_profile) {
+		sa.name = ns_name;
+		sa.name2 = name;
+		sa.name3 = old_profile ? old_profile->name :
+			"unconfined";
+		aa_audit_status(NULL, &sa);
+	} else {
+		if (old_profile) {
+			sa.name = "unconfined";
+			sa.name2 = old_profile->name;
+			aa_audit_status(NULL, &sa);
+		} else {
+			sa.info = "task is unconfined";
+			aa_audit_status(NULL, &sa);
+		}
+	}
+	aa_put_namespace(ns);
+	aa_put_profile(old_profile);
+	aa_put_profile(new_profile);
+	return 0;
+}

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 45/47] Add AppArmor LSM to security/Makefile
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (43 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 44/47] AppArmor: all the rest John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 46/47] add simple network toggles to apparmor John, Johansen
  2007-12-20 14:09 ` [AppArmor 47/47] --- Documentation/lsm/AppArmor-Security-Goal.txt | 134 +++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) John, Johansen
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm
  Cc: linux-kernel, linux-security-module, John Johansen,
	Andreas Gruenbacher

[-- Attachment #1: apparmor-intree.diff --]
[-- Type: text/plain, Size: 2096 bytes --]

Signed-off-by: John Johansen <jjohansen@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>

---
 security/Kconfig          |    1 +
 security/Makefile         |    1 +
 security/apparmor/Kconfig |   22 ++++++++++++++++++++--
 3 files changed, 22 insertions(+), 2 deletions(-)

--- a/security/Kconfig
+++ b/security/Kconfig
@@ -105,6 +105,7 @@ config SECURITY_ROOTPLUG
 
 source security/selinux/Kconfig
 source security/smack/Kconfig
+source security/apparmor/Kconfig
 
 endmenu
 
--- a/security/Makefile
+++ b/security/Makefile
@@ -16,5 +16,6 @@ obj-$(CONFIG_SECURITY)			+= security.o d
 # Must precede capability.o in order to stack properly.
 obj-$(CONFIG_SECURITY_SELINUX)		+= selinux/built-in.o
 obj-$(CONFIG_SECURITY_SMACK)		+= commoncap.o smack/built-in.o
+obj-$(CONFIG_SECURITY_APPARMOR)		+= commoncap.o apparmor/
 obj-$(CONFIG_SECURITY_CAPABILITIES)	+= commoncap.o capability.o
 obj-$(CONFIG_SECURITY_ROOTPLUG)		+= commoncap.o root_plug.o
--- a/security/apparmor/Kconfig
+++ b/security/apparmor/Kconfig
@@ -1,9 +1,27 @@
 config SECURITY_APPARMOR
-	tristate "AppArmor support"
-	depends on SECURITY!=n
+	bool "AppArmor support"
+	depends on SECURITY
+	select AUDIT
 	help
 	  This enables the AppArmor security module.
 	  Required userspace tools (if they are not included in your
 	  distribution) and further information may be found at
 	  <http://forge.novell.com/modules/xfmod/project/?apparmor>
+
 	  If you are unsure how to answer this question, answer N.
+
+config SECURITY_APPARMOR_BOOTPARAM_VALUE
+	int "AppArmor boot parameter default value"
+	depends on SECURITY_APPARMOR
+	range 0 1
+	default 1
+	help
+	  This option sets the default value for the kernel parameter
+	  'apparmor', which allows AppArmor to be enabled or disabled
+          at boot.  If this option is set to 0 (zero), the AppArmor
+	  kernel parameter will default to 0, disabling AppArmor at
+	  bootup.  If this option is set to 1 (one), the AppArmor
+	  kernel parameter will default to 1, enabling AppArmor at
+	  bootup.
+
+	  If you are unsure how to answer this question, answer 1.

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 46/47] add simple network toggles to apparmor
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (44 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 45/47] Add AppArmor LSM to security/Makefile John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  2007-12-20 14:09 ` [AppArmor 47/47] --- Documentation/lsm/AppArmor-Security-Goal.txt | 134 +++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) John, Johansen
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm; +Cc: linux-kernel, linux-security-module, John Johansen, Jesse Michael

[-- Attachment #1: apparmor-network.diff --]
[-- Type: text/plain, Size: 10300 bytes --]

Signed-off-by: John Johansen <jjohansen@suse.de>
Signed-off-by: Jesse Michael <jmichael@suse.de>

---
 security/apparmor/Makefile           |    7 +
 security/apparmor/apparmor.h         |    7 +
 security/apparmor/lsm.c              |  129 ++++++++++++++++++++++++++++++++++-
 security/apparmor/main.c             |   96 ++++++++++++++++++++++++++
 security/apparmor/module_interface.c |   20 +++++
 5 files changed, 255 insertions(+), 4 deletions(-)

--- a/security/apparmor/Makefile
+++ b/security/apparmor/Makefile
@@ -8,6 +8,11 @@ apparmor-y := main.o list.o procattr.o l
 quiet_cmd_make-caps = GEN     $@
 cmd_make-caps = sed -n -e "/CAP_FS_MASK/d" -e "s/^\#define[ \\t]\\+CAP_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\$$/[\\2]  = \"\\1\",/p" $< | tr A-Z a-z > $@
 
-$(obj)/main.o : $(obj)/capability_names.h
+quiet_cmd_make-af = GEN     $@
+cmd_make-af = sed -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "s/^\#define[ \\t]\\+AF_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/[\\2]  = \"\\1\",/p" $< | tr A-Z a-z > $@
+
+$(obj)/main.o : $(obj)/capability_names.h $(obj)/af_names.h
 $(obj)/capability_names.h : $(srctree)/include/linux/capability.h
 	$(call cmd,make-caps)
+$(obj)/af_names.h : $(srctree)/include/linux/socket.h
+	$(call cmd,make-af)
--- a/security/apparmor/apparmor.h
+++ b/security/apparmor/apparmor.h
@@ -16,6 +16,8 @@
 #include <linux/fs.h>
 #include <linux/binfmts.h>
 #include <linux/rcupdate.h>
+#include <linux/socket.h>
+#include <net/sock.h>
 
 /*
  * We use MAY_READ, MAY_WRITE, MAY_EXEC, MAY_APPEND and the following flags
@@ -169,6 +171,7 @@ struct aa_profile {
 	struct list_head task_contexts;
 	spinlock_t lock;
 	unsigned long int_flags;
+	u16 network_families[AF_MAX];
 };
 
 extern struct list_head profile_ns_list;
@@ -215,6 +218,7 @@ struct aa_audit {
 	int request_mask, denied_mask;
 	struct iattr *iattr;
 	pid_t task, parent;
+	int family, type, protocol;
 	int error_code;
 };
 
@@ -276,6 +280,9 @@ extern void aa_change_task_context(struc
 				   struct aa_profile *previous_profile);
 extern int aa_may_ptrace(struct aa_task_context *cxt,
 			 struct aa_profile *tracee);
+extern int aa_net_perm(struct aa_profile *profile, char *operation,
+			int family, int type, int protocol);
+extern int aa_revalidate_sk(struct sock *sk, char *operation);
 
 /* list.c */
 extern struct aa_namespace *__aa_find_namespace(const char *name,
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -18,6 +18,7 @@
 #include <linux/ctype.h>
 #include <linux/sysctl.h>
 #include <linux/audit.h>
+#include <net/sock.h>
 
 #include "apparmor.h"
 #include "inline.h"
@@ -616,6 +617,117 @@ static void apparmor_task_free_security(
 	aa_release(task);
 }
 
+static int apparmor_socket_create(int family, int type, int protocol, int kern)
+{
+	struct aa_profile *profile;
+	int error = 0;
+
+	if (kern)
+		return 0;
+
+	profile = aa_get_profile(current);
+	if (profile)
+		error = aa_net_perm(profile, "socket_create", family,
+							type, protocol);
+	aa_put_profile(profile);
+
+	return error;
+}
+
+static int apparmor_socket_post_create(struct socket *sock, int family,
+					int type, int protocol, int kern)
+{
+	struct sock *sk = sock->sk;
+
+	if (kern)
+		return 0;
+
+	return aa_revalidate_sk(sk, "socket_post_create");
+}
+
+static int apparmor_socket_bind(struct socket *sock,
+				struct sockaddr *address, int addrlen)
+{
+	struct sock *sk = sock->sk;
+
+	return aa_revalidate_sk(sk, "socket_bind");
+}
+
+static int apparmor_socket_connect(struct socket *sock,
+					struct sockaddr *address, int addrlen)
+{
+	struct sock *sk = sock->sk;
+
+	return aa_revalidate_sk(sk, "socket_connect");
+}
+
+static int apparmor_socket_listen(struct socket *sock, int backlog)
+{
+	struct sock *sk = sock->sk;
+
+	return aa_revalidate_sk(sk, "socket_listen");
+}
+
+static int apparmor_socket_accept(struct socket *sock, struct socket *newsock)
+{
+	struct sock *sk = sock->sk;
+
+	return aa_revalidate_sk(sk, "socket_accept");
+}
+
+static int apparmor_socket_sendmsg(struct socket *sock,
+					struct msghdr *msg, int size)
+{
+	struct sock *sk = sock->sk;
+
+	return aa_revalidate_sk(sk, "socket_sendmsg");
+}
+
+static int apparmor_socket_recvmsg(struct socket *sock,
+				   struct msghdr *msg, int size, int flags)
+{
+	struct sock *sk = sock->sk;
+
+	return aa_revalidate_sk(sk, "socket_recvmsg");
+}
+
+static int apparmor_socket_getsockname(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+
+	return aa_revalidate_sk(sk, "socket_getsockname");
+}
+
+static int apparmor_socket_getpeername(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+
+	return aa_revalidate_sk(sk, "socket_getpeername");
+}
+
+static int apparmor_socket_getsockopt(struct socket *sock, int level,
+					int optname)
+{
+	struct sock *sk = sock->sk;
+
+	return aa_revalidate_sk(sk, "socket_getsockopt");
+}
+
+static int apparmor_socket_setsockopt(struct socket *sock, int level,
+					int optname)
+{
+	struct sock *sk = sock->sk;
+
+	return aa_revalidate_sk(sk, "socket_setsockopt");
+}
+
+static int apparmor_socket_shutdown(struct socket *sock, int how)
+{
+	struct sock *sk = sock->sk;
+
+	return aa_revalidate_sk(sk, "socket_shutdown");
+}
+
 static int apparmor_getprocattr(struct task_struct *task, char *name,
 				char **value)
 {
@@ -716,9 +828,6 @@ struct security_operations apparmor_ops 
 	.capable =			apparmor_capable,
 	.syslog =			cap_syslog,
 
-	.netlink_send =			cap_netlink_send,
-	.netlink_recv =			cap_netlink_recv,
-
 	.bprm_apply_creds =		cap_bprm_apply_creds,
 	.bprm_set_security =		apparmor_bprm_set_security,
 	.bprm_secureexec =		apparmor_bprm_secureexec,
@@ -754,6 +863,20 @@ struct security_operations apparmor_ops 
 
 	.getprocattr =			apparmor_getprocattr,
 	.setprocattr =			apparmor_setprocattr,
+
+	.socket_create =		apparmor_socket_create,
+	.socket_post_create =		apparmor_socket_post_create,
+	.socket_bind =			apparmor_socket_bind,
+	.socket_connect =		apparmor_socket_connect,
+	.socket_listen =		apparmor_socket_listen,
+	.socket_accept =		apparmor_socket_accept,
+	.socket_sendmsg =		apparmor_socket_sendmsg,
+	.socket_recvmsg =		apparmor_socket_recvmsg,
+	.socket_getsockname =		apparmor_socket_getsockname,
+	.socket_getpeername =		apparmor_socket_getpeername,
+	.socket_getsockopt =		apparmor_socket_getsockopt,
+	.socket_setsockopt =		apparmor_socket_setsockopt,
+	.socket_shutdown =		apparmor_socket_shutdown,
 };
 
 static void info_message(const char *str)
--- a/security/apparmor/main.c
+++ b/security/apparmor/main.c
@@ -14,6 +14,9 @@
 #include <linux/audit.h>
 #include <linux/mount.h>
 #include <linux/ptrace.h>
+#include <linux/socket.h>
+#include <linux/net.h>
+#include <net/sock.h>
 
 #include "apparmor.h"
 
@@ -341,6 +344,24 @@ static void aa_audit_file_mask(struct au
 	audit_log_format(ab, " %s=\"%s::%s\"", name, user, other);
 }
 
+static const char *address_families[] = {
+#include "af_names.h"
+};
+
+static const char *sock_types[] = {
+	"unknown(0)",
+	"stream",
+	"dgram",
+	"raw",
+	"rdm",
+	"seqpacket",
+	"dccp",
+	"unknown(7)",
+	"unknown(8)",
+	"unknown(9)",
+	"packet",
+};
+
 /**
  * aa_audit - Log an audit event to the audit subsystem
  * @profile: profile to check against
@@ -406,6 +427,24 @@ static int aa_audit_base(struct aa_profi
 		audit_log_untrustedstring(ab, sa->name2);
 	}
 
+	if (sa->family || sa->type) {
+		if (address_families[sa->family])
+			audit_log_format(ab, " family=\"%s\"",
+					 address_families[sa->family]);
+		else
+			audit_log_format(ab, " family=\"unknown(%d)\"",
+					 sa->family);
+
+		if (sock_types[sa->type])
+			audit_log_format(ab, " sock_type=\"%s\"",
+					 sock_types[sa->type]);
+		else
+			audit_log_format(ab, " sock_type=\"unknown(%d)\"",
+					 sa->type);
+
+		audit_log_format(ab, " protocol=%d", sa->protocol);
+	}
+
 	audit_log_format(ab, " pid=%d", current->pid);
 
 	if (profile) {
@@ -725,6 +764,63 @@ int aa_link(struct aa_profile *profile,
 	return error;
 }
 
+int aa_net_perm(struct aa_profile *profile, char *operation,
+		int family, int type, int protocol)
+{
+	struct aa_audit sa;
+	int error = 0;
+	u16 family_mask;
+
+	if ((family < 0) || (family >= AF_MAX))
+		return -EINVAL;
+
+	if ((type < 0) || (type >= SOCK_MAX))
+		return -EINVAL;
+
+	/* unix domain and netlink sockets are handled by ipc */
+	if (family == AF_UNIX || family == AF_NETLINK)
+		return 0;
+
+	family_mask = profile->network_families[family];
+
+	error = (family_mask & (1 << type)) ? 0 : -EACCES;
+
+	memset(&sa, 0, sizeof(sa));
+	sa.operation = operation;
+	sa.gfp_mask = GFP_KERNEL;
+	sa.family = family;
+	sa.type = type;
+	sa.protocol = protocol;
+	sa.error_code = error;
+
+	error = aa_audit(profile, &sa);
+
+	return error;
+}
+
+int aa_revalidate_sk(struct sock *sk, char *operation)
+{
+	struct aa_profile *profile;
+	int error = 0;
+
+	/* this is some debugging code to flush out the network hooks that
+	   that are called in interrupt context */
+	if (in_interrupt()) {
+		printk("AppArmor Debug: Hook being called from interrupt context\n");
+		dump_stack();
+		return 0;
+	}
+
+	profile = aa_get_profile(current);
+	if (profile)
+		error = aa_net_perm(profile, operation,
+				    sk->sk_family, sk->sk_type,
+				    sk->sk_protocol);
+	aa_put_profile(profile);
+
+	return error;
+}
+
 /*******************************
  * Global task related functions
  *******************************/
--- a/security/apparmor/module_interface.c
+++ b/security/apparmor/module_interface.c
@@ -283,6 +283,8 @@ static struct aa_profile *aa_unpack_prof
 {
 	struct aa_profile *profile = NULL;
 	struct aa_audit sa;
+	size_t size = 0;
+	int i;
 
 	int error = -EPROTO;
 
@@ -311,6 +313,24 @@ static struct aa_profile *aa_unpack_prof
 	if (!aa_is_u32(e, &(profile->capabilities), NULL))
 		goto fail;
 
+	size = aa_is_array(e, "net_allowed_af");
+	if (size) {
+		if (size > AF_MAX)
+			goto fail;
+
+		for (i = 0; i < size; i++) {
+			if (!aa_is_u16(e, &profile->network_families[i], NULL))
+				goto fail;
+		}
+		if (!aa_is_nameX(e, AA_ARRAYEND, NULL))
+			goto fail;
+		/* allow unix domain and netlink sockets they are handled
+		 * by IPC
+		 */
+	}
+	profile->network_families[AF_UNIX] = 0xffff;
+	profile->network_families[AF_NETLINK] = 0xffff;
+
 	/* get file rules */
 	profile->file_rules = aa_unpack_dfa(e);
 	if (IS_ERR(profile->file_rules)) {

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [AppArmor 47/47] --- Documentation/lsm/AppArmor-Security-Goal.txt | 134 +++++++++++++++++++++++++++ 1 file changed, 134 insertions(+)
  2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
                   ` (45 preceding siblings ...)
  2007-12-20 14:09 ` [AppArmor 46/47] add simple network toggles to apparmor John, Johansen
@ 2007-12-20 14:09 ` John, Johansen
  46 siblings, 0 replies; 48+ messages in thread
From: John, Johansen @ 2007-12-20 14:09 UTC (permalink / raw)
  To: akpm; +Cc: linux-kernel, linux-security-module

[-- Attachment #1: apparmor-security-goal.diff --]
[-- Type: text/plain, Size: 7991 bytes --]

--- /dev/null
+++ b/Documentation/lsm/AppArmor-Security-Goal.txt
@@ -0,0 +1,134 @@
+AppArmor Security Goal
+Crispin Cowan, PhD
+MercenaryLinux.com
+
+This document specifies the security goal that AppArmor is intended to
+achieve, so that users can evaluate whether AppArmor will meet their
+needs, and kernel developers can evaluate whether AppArmor is living up
+to its claims. This document is *not* a general purpose explanation of
+how AppArmor works, nor is it an explanation for why one might want to
+use AppArmor rather than some other system.
+
+AppArmor is intended to limit system damage from attackers exploiting
+vulnerabilities in applications that the system hosts. The threat is
+that an attacker can cause a vulnerable application to do something
+unexpected and undesirable. AppArmor addresses this threat by confining
+the application to access only the resources it needs to access to
+execute properly, effectively imposing "least privilege" execution on
+the application.
+
+Applications interact with the rest of the system via resources
+including files, interprocess communication, networking, capabilities,
+and execution of other applications. The purpose of least privilege is
+to bound the damage that a malicious user or code can do by removing
+access to resources that the application does not need for its intended
+function. This is true for all access control systems, including AppArmor.
+
+The "attacker" is someone trying to gain the privileges of a process for
+themselves. For instance, a policy for a web server might grant read
+only access to most web documents, preventing an attacker who can
+corrupt the web server from defacing the web pages. A web server has
+access to the web server's local file system, and a network attacker
+trying to hack the web server does not have such file access. An e-mail
+attacker attempting to infect the recipient of the e-mail does not have
+access to the files that the victim user's mail client does. By limiting
+the scope of access for an application, AppArmor can limit the damage an
+attacker can do by exploiting vulnerabilities in applications.
+
+An "application" is one or more related processes performing a function,
+e.g. the gang of processes that constitute an Apache web server, or a
+Postfix mail server. AppArmor *only* confines processes that the
+AppArmor policy says it should confine, and other processes are
+permitted to do anything that DAC permits. This is sometimes known as a
+targeted security policy.
+
+AppArmor does not provide a "default" policy that applies to all
+processes. So to defend an entire host, you have to piece-wise confine
+each process that is exposed to potential attack. For instance, to
+defend a system against network attack, place AppArmor profiles around
+every application that accesses the network. This limits the damage a
+network attacker can do to the file system to only those files granted
+by the profiles for the network-available applications. Similarly, to
+defend a system against attack from the console, place AppArmor profiles
+around every application that accessed the keyboard and mouse. The
+system is "defended" in that the worst the attacker can do to corrupt
+the system is limited to the transitive closure of what the confined
+processes are allowed to access.
+
+AppArmor currently mediates access to files, ability to use POSIX.1e
+Capabilities, and coarse-grained control on network access. This is
+sufficient to prevent a confined process from *directly* corrupting the
+file system. It is not sufficient to prevent a confined process from
+*indirectly* corrupting the system by influencing some other process to
+do the dirty deed. But to do so requires a complicit process that can be
+manipulated through another channel such as IPC. A "complicit" process
+is either a malicious process the attacker somehow got control of, or is
+a process that is actively listening to IPC of some kind and can be
+corrupted via IPC.
+
+The only IPC that AppArmor mediates is access to named sockets, FIFOs,
+etc. that appear in the file system name space, a side effect of
+AppArmor's file access mediation. Future versions of AppArmor will
+mediate more resources, including finer grained network access controls,
+and controls on various forms of IPC.
+
+AppArmor specifies the programs to be confined and the resources they
+can access in a syntax similar to how users are accustomed to accessing
+those resources. So file access controls are specified using absolute
+paths with respect to the name space the process is in. POSIX.1e
+capabilities are specified by name. Network access controls currently
+are specified by simply naming the protocol that can be used e.g. tcp,
+udp, and in the future will be more general, resembling firewall rules.
+
+Thus the AppArmor security goal should be considered piecewise from the
+point of view of a single confined process: that process should only be
+able to access the resources specified in its profile:
+
+    * can only access files that are reachable in its name space by path
+      names matching its profile, and only with the permitted modes:
+      read, append, write, memory map, execute, and link
+    * can only use the POSIX.1e capabilities listed in the profile
+    * can only perform the network operations listed in the profile
+
+Security issues that AppArmor explicitly does *not* address:
+
+    * Processes that are not confined by AppArmor are not restricted in
+      any way by AppArmor. If an unconfined process is considered an
+      unacceptable threat, then confine additional applications until
+      adequate security is achieved.
+    * A process that is not permitted to directly access a resource can
+      influence some other process that does have access to the resource
+      may do so, if the "influence" is a permitted action.
+    * A confined process may only access a file if it has at least one
+      of the files aliases specified in its profile.  If a file alias is
+      not specified in the profile then it can not be accessed by that
+      path.  The creation of aliases needs to be tightly controlled in
+      confined applications, hard links creation should be limited to
+      provide adequate security.
+    * A confined process can operate on a file descriptor passed to it
+      by an unconfined process, even if it manipulates a file not in the
+      confined process's profile. To block this attack, confine the
+      process that passed the file descriptor.
+    * Process activities not currently mediated by AppArmor are
+      permitted, e.g. confined processes can perform any IPC that DAC
+      permits, other than signals as mediated by CAP_KILL.
+    * Manipulating AppArmor policy requires being both root privileged
+      and not being confined by AppArmor, thus there is explicitly no
+      capability for non-privileged users to change AppArmor policy.
+    * AppArmor confines processes if they are children of a confined
+      process, or if the name of the exec'd child matches the name of an
+      AppArmor profile. Another process could copy a program to a
+      different path name and then execute it without confinement, but
+      the other process would have to have permission to do so in the
+      first place. To prevent this, confine the other process and
+      additional applications until adequate security is achieved.
+    * Mount and namespace manipulations can be used to arbitrarily
+      change the pathnames that files appear at, and thus completely
+      bypass AppArmor policy. To prevent this, processes confined by
+      AppArmor are currently not permitted to call mount or manipulate
+      name spaces at all. A future development may provide more granular
+      controls on mount and namespace manipulations.
+    * AppArmor does not slice bread, cure cancer, bring world peace, or
+      provide perfect security. This list may be expanded :-)
+
+

-- 


^ permalink raw reply	[flat|nested] 48+ messages in thread

end of thread, other threads:[~2007-12-20 14:34 UTC | newest]

Thread overview: 48+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-12-20 14:09 [AppArmor 00/47] AppArmor security module overview John, Johansen
2007-12-20 14:09 ` [AppArmor 01/47] Pass struct vfsmount to the inode_create LSM hook John, Johansen
2007-12-20 14:09 ` [AppArmor 02/47] Pass struct path down to remove_suid and children John, Johansen
2007-12-20 14:09 ` [AppArmor 03/47] Add a vfsmount parameter to notify_change() John, Johansen
2007-12-20 14:09 ` [AppArmor 04/47] Pass struct vfsmount to the inode_setattr LSM hook John, Johansen
2007-12-20 14:09 ` [AppArmor 05/47] Add struct vfsmount parameter to vfs_mkdir() John, Johansen
2007-12-20 14:09 ` [AppArmor 06/47] Pass struct vfsmount to the inode_mkdir LSM hook John, Johansen
2007-12-20 14:09 ` [AppArmor 07/47] Add a struct vfsmount parameter to vfs_mknod() John, Johansen
2007-12-20 14:09 ` [AppArmor 08/47] Pass struct vfsmount to the inode_mknod LSM hook John, Johansen
2007-12-20 14:09 ` [AppArmor 09/47] Add a struct vfsmount parameter to vfs_symlink() John, Johansen
2007-12-20 14:09 ` [AppArmor 10/47] Pass struct vfsmount to the inode_symlink LSM hook John, Johansen
2007-12-20 14:09 ` [AppArmor 11/47] Pass struct vfsmount to the inode_readlink " John, Johansen
2007-12-20 14:09 ` [AppArmor 12/47] Add struct vfsmount parameters to vfs_link() John, Johansen
2007-12-20 14:09 ` [AppArmor 13/47] Pass the struct vfsmounts to the inode_link LSM hook John, Johansen
2007-12-20 14:09 ` [AppArmor 14/47] Add a struct vfsmount parameter to vfs_rmdir() John, Johansen
2007-12-20 14:09 ` [AppArmor 15/47] Pass struct vfsmount to the inode_rmdir LSM hook John, Johansen
2007-12-20 14:09 ` [AppArmor 16/47] Call lsm hook before unhashing dentry in vfs_rmdir() John, Johansen
2007-12-20 14:09 ` [AppArmor 17/47] Add a struct vfsmount parameter to vfs_unlink() John, Johansen
2007-12-20 14:09 ` [AppArmor 18/47] Pass struct vfsmount to the inode_unlink LSM hook John, Johansen
2007-12-20 14:09 ` [AppArmor 19/47] Add struct vfsmount parameters to vfs_rename() John, Johansen
2007-12-20 14:09 ` [AppArmor 20/47] Pass struct vfsmount to the inode_rename LSM hook John, Johansen
2007-12-20 14:09 ` [AppArmor 21/47] Add a struct vfsmount parameter to vfs_setxattr() John, Johansen
2007-12-20 14:09 ` [AppArmor 22/47] Pass struct vfsmount to the inode_setxattr LSM hook John, Johansen
2007-12-20 14:09 ` [AppArmor 23/47] Add a struct vfsmount parameter to vfs_getxattr() John, Johansen
2007-12-20 14:09 ` [AppArmor 24/47] Pass struct vfsmount to the inode_getxattr LSM hook John, Johansen
2007-12-20 14:09 ` [AppArmor 25/47] Add a struct vfsmount parameter to vfs_listxattr() John, Johansen
2007-12-20 14:09 ` [AppArmor 26/47] Pass struct vfsmount to the inode_listxattr LSM hook John, Johansen
2007-12-20 14:09 ` [AppArmor 27/47] Add a struct vfsmount parameter to vfs_removexattr() John, Johansen
2007-12-20 14:09 ` [AppArmor 28/47] Pass struct vfsmount to the inode_removexattr LSM hook John, Johansen
2007-12-20 14:09 ` [AppArmor 29/47] Fix __d_path() for lazy unmounts and make it unambiguous John, Johansen
2007-12-20 14:09 ` [AppArmor 30/47] Make d_path() consistent across mount operations John, Johansen
2007-12-20 14:09 ` [AppArmor 31/47] Add d_namespace_path() to compute namespace relative pathnames John, Johansen
2007-12-20 14:09 ` [AppArmor 32/47] From: Miklos Szeredi <mszeredi@suse.cz> John, Johansen
2007-12-20 14:09 ` [AppArmor 33/47] VFS: new fsetattr() file operation John, Johansen
2007-12-20 14:09 ` [AppArmor 34/47] Pass struct file down the inode_*xattr security LSM hooks John, Johansen
2007-12-20 14:09 ` [AppArmor 35/47] Factor out sysctl pathname code John, Johansen
2007-12-20 14:09 ` [AppArmor 36/47] Allow permission functions to tell between parent and leaf checks John, Johansen
2007-12-20 14:09 ` [AppArmor 37/47] Switch to vfs_permission() in do_path_lookup() John, Johansen
2007-12-20 14:09 ` [AppArmor 38/47] Switch to vfs_permission() in sys_fchdir() John, Johansen
2007-12-20 14:09 ` [AppArmor 39/47] Fix file_permission() John, Johansen
2007-12-20 14:09 ` [AppArmor 40/47] Export audit subsystem for use by modules John, Johansen
2007-12-20 14:09 ` [AppArmor 41/47] AppArmor: Main Part John, Johansen
2007-12-20 14:09 ` [AppArmor 42/47] AppArmor: Module and LSM hooks John, Johansen
2007-12-20 14:09 ` [AppArmor 43/47] AppArmor: Profile loading and manipulation, pathname matching John, Johansen
2007-12-20 14:09 ` [AppArmor 44/47] AppArmor: all the rest John, Johansen
2007-12-20 14:09 ` [AppArmor 45/47] Add AppArmor LSM to security/Makefile John, Johansen
2007-12-20 14:09 ` [AppArmor 46/47] add simple network toggles to apparmor John, Johansen
2007-12-20 14:09 ` [AppArmor 47/47] --- Documentation/lsm/AppArmor-Security-Goal.txt | 134 +++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) John, Johansen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox