lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Tue, 30 Sep 2008 10:45:53 -0500
From:	"Serge E. Hallyn" <serue@...ibm.com>
To:	Kentaro Takeda <takedakn@...data.co.jp>
Cc:	linux-security-module@...r.kernel.org,
	linux-kernel@...r.kernel.org, haradats@...data.co.jp,
	Tetsuo Handa <penguin-kernel@...ove.SAKURA.ne.jp>,
	Al Viro <viro@...IV.linux.org.uk>
Subject: Re: [TOMOYO #9 (2.6.27-rc7-mm1) 1/6] LSM adapter functions.

Quoting Kentaro Takeda (takedakn@...data.co.jp):
> Serge E. Hallyn wrote:
> > Unfortunately I think that is a shortcoming in the security_path_*
> > patchset.  Unfortunate bc that is going to be a pain to work out.
> Thanks for your constructive and tough suggestion. ;-)
> 
> > So for starters,
> > both vfs_mknod and vfs_create do may_create, so just pull that
> > into the callers.
> Do you mean that we should move DAC code to all the caller of vfs_* ? 

That's not reasonable, is it.

The rule thus far has been 'DAC before MAC'.  Question to all:  do we
insist on keeping it that way?

If the answer is yes, then the security_path_hooks patch is inherently
wrong.

If the answer is no, then Kentaro doesn't need to resort to this
ugliness to try and get may_delete() called before his MAC code, only to
have may_delete() called a second time from the vfs_* functions.

-serge

> If we move DAC code to the caller of vfs_*(), we need not to 
> introduce seucrity_path_*() because we can move security_inode_*() 
> together. Furthermore, each filesystem must perform DAC by itself. It 
> will mess up the filesystem code...
> 
> > But I do think it needs to be worked out in the core code, not in
> > Tomoyo (and each lsm using security_path_*).  So for starters,
> > both vfs_mknod and vfs_create do may_create, so just pull that
> > into the callers.  Now Al or Christoph may yell NO due to the
> > intended layering (which i'm not clear on), in which case the
> > solution will be tougher.
> There are two approaches to perform DAC before MAC using 
> security_path_*(). One is cloning DAC functions in 
> security/security.c . The other is modifying fs/namei.c to make DAC 
> functions visible to security/security.c . Which approach is 
> preferable?
> 
> The attached patch is an implementation of the former approach. If 
> CONFIG_SECURITY_PATH is not defined, cloned DAC functions will not be 
> compiled.
> 
> Regards,
> 
> ---
> Subject: vfs: introduce new LSM hooks where vfsmount is available.
> 
> ----- What is this patch for? -----
> 
> There are security_inode_*() LSM hooks for attribute-based MAC, but they are not
> suitable for pathname-based MAC because they don't receive "struct vfsmount"
> information.
> 
> ----- How this patch was developed? -----
> 
> Two pathname-based MACs, AppArmor and TOMOYO Linux, are trying to merge
> upstream. But because of "struct vfsmount" problem, they have been unable to
> merge upstream.
> 
> Here are the list of approaches and the reasons of denial.
> 
> (1) Not using LSM
>  http://lwn.net/Articles/277833/
> 
>  This approach was rejected because security modules should use LSM because the
>  whole idea behind LSM was to have a single set of hooks for all security
>  modules; if every module now adds its own set of hooks, that purpose will have
>  been defeated and the kernel will turn into a big mess of security hooks.
> 
> (2) Retrieving "struct vfsmount" from "struct task_struct".
>  http://lkml.org/lkml/2007/11/5/388
> 
>  Since "struct task_struct" contains list of "struct vfsmount",
>  "struct vfsmount" which corresponds to "struct dentry" can be retrieved from
>  the list unless "mount --bind" is used.
> 
>  This approach turned out to cause a critical problem that getting namespace_sem
>  lock from security_inode_*() triggers AB-BA deadlock.
> 
> (3) Adding "struct vfsmount" parameter to VFS helper functions.
>  http://lkml.org/lkml/2008/5/29/207
> 
>  This approach adds "struct vfsmount" to VFS helper functions (e.g. vfs_mkdir()
>  and vfs_symlink()) and LSM hooks inside VFS helper functions. This approach is
>  helpful for not only AppArmor and TOMOYO Linux 2.x but also SELinux and
>  auditing purpose, for this approach allows existent LSM users to use pathnames
>  in their access control and audit logs.
> 
>  This approach was rejected by Al Viro, the VFS maintainer, because he thinks
>  individual filesystem should remain "struct vfsmount"-unaware and VFS helper
>  functions should not receive "struct vfsmount".
> 
>  Al Viro also suggested to move existing security_inode_*() to out of VFS
>  helper functions so that security_inode_*() can receive "struct vfsmount"
>  without modifying VFS helper functions, but this suggestion was opposed by
>  Stephen Smalley because changing the order of permission checks (i.e.
>  MAC checks before DAC checks) is not acceptable.
> 
> (4) Passing "struct vfsmount" via "struct task_struct".
>  http://lkml.org/lkml/2007/11/16/157
> 
>  Since we didn't understand the reason why accessing "struct vfsmount" from
>  LSM hooks inside VFS helper functions is not acceptable, we thought the reason
>  why VFS helper functions don't receive "struct vfsmount" is the amount of
>  modifications needed to do so. Thus, we proposed to pass "struct vfsmount" via
>  "struct task_struct" so that modifications remain minimal.
> 
>  This approach was rejected because this is an abuse of "struct task_struct".
> 
> (5) Remembering pathname of "struct vfsmount" via "struct task_struct".
>  http://lkml.org/lkml/2008/8/19/16
> 
>  Since pathname of a "struct dentry" up to the mount point can be calculated
>  without "struct vfsmount", absolute pathname of a "struct dentry" can be
>  calculated if "struct task_struct" can remember absolute pathname of a
>  "struct vfsmount" which corresponds to "struct dentry".
>  As we now understand that Al Viro is opposing to access "struct vfsmount" from
>  LSM hooks inside VFS helper functions, we gave up delivering "struct vfsmount"
>  to LSM hooks inside VFS helper functions.
>  Kernel 2.6.26 introduced read-only bind mount feature, and hooks for that
>  feature (i.e. mnt_want_write() and mnt_drop_write()) were inserted around
>  VFS helper functions call. Since mnt_want_write() receives "struct vfsmount"
>  which corresponds to "struct dentry" that will be passed to subsequent VFS
>  helper functions call, we associated pathname of "struct vfsmount" with
>  "struct task_struct" instead of associating "struct vfsmount" itself.
> 
>  This approach was not explicitly rejected, but there seems to be performance
>  problem.
> 
> (6) Introducing new LSM hooks.
>  (this patch)
> 
>  We understand that adding new LSM hooks which receive "struct vfsmount" outside
>  VFS helper functions is the most straightforward approach. This approach has
>  less impact to existing LSM module and no impact to VFS helper functions.
> 
> 
> Signed-off-by: Kentaro Takeda <takedakn@...data.co.jp>
> Signed-off-by: Tetsuo Handa <penguin-kernel@...ove.SAKURA.ne.jp>
> Signed-off-by: Toshiharu Harada <haradats@...data.co.jp>
> Cc: Al Viro <viro@...iv.linux.org.uk>
> Cc: Christoph Hellwig <hch@....de>
> Cc: Crispin Cowan <crispin@...spincowan.com>
> Cc: Stephen Smalley <sds@...ho.nsa.gov>
> Cc: Casey Schaufler <casey@...aufler-ca.com>
> Signed-off-by: Andrew Morton <akpm@...ux-foundation.org>
> -----
> 
>  fs/namei.c               |   37 +++++++
>  fs/open.c                |    5 +
>  include/linux/security.h |  139 ++++++++++++++++++++++++++++
>  net/unix/af_unix.c       |    4 
>  security/Kconfig         |    9 +
>  security/capability.c    |   57 +++++++++++
>  security/security.c      |  230 +++++++++++++++++++++++++++++++++++++++++++++++
>  7 files changed, 481 insertions(+)
> 
> --- linux-2.6.27-rc7-mm1.orig/fs/namei.c
> +++ linux-2.6.27-rc7-mm1/fs/namei.c
> @@ -1571,6 +1571,10 @@ int may_open(struct nameidata *nd, int a
>  		 * Refuse to truncate files with mandatory locks held on them.
>  		 */
>  		error = locks_verify_locked(inode);
> +		if (!error)
> +			error = security_path_truncate(&nd->path, 0,
> +					       ATTR_MTIME|ATTR_CTIME|ATTR_OPEN,
> +						       NULL);
>  		if (!error) {
>  			DQUOT_INIT(inode);
> 
> @@ -1601,7 +1605,11 @@ static int __open_namei_create(struct na
> 
>  	if (!IS_POSIXACL(dir->d_inode))
>  		mode &= ~current->fs->umask;
> +	error = security_path_mknod(&nd->path, path->dentry, mode, 0);
> +	if (error)
> +		goto out_unlock;
>  	error = vfs_create(dir->d_inode, path->dentry, mode, nd);
> +out_unlock:
>  	mutex_unlock(&dir->d_inode->i_mutex);
>  	dput(nd->path.dentry);
>  	nd->path.dentry = path->dentry;
> @@ -2014,6 +2022,9 @@ asmlinkage long sys_mknodat(int dfd, con
>  	error = mnt_want_write(nd.path.mnt);
>  	if (error)
>  		goto out_dput;
> +	error = security_path_mknod(&nd.path, dentry, mode, dev);
> +	if (error)
> +		goto out_drop_write;
>  	switch (mode & S_IFMT) {
>  		case 0: case S_IFREG:
>  			error = vfs_create(nd.path.dentry->d_inode,dentry,mode,&nd);
> @@ -2026,6 +2037,7 @@ asmlinkage long sys_mknodat(int dfd, con
>  			error = vfs_mknod(nd.path.dentry->d_inode,dentry,mode,0);
>  			break;
>  	}
> +out_drop_write:
>  	mnt_drop_write(nd.path.mnt);
>  out_dput:
>  	dput(dentry);
> @@ -2085,7 +2097,11 @@ asmlinkage long sys_mkdirat(int dfd, con
>  	error = mnt_want_write(nd.path.mnt);
>  	if (error)
>  		goto out_dput;
> +	error = security_path_mkdir(&nd.path, dentry, mode);
> +	if (error)
> +		goto out_drop_write;
>  	error = vfs_mkdir(nd.path.dentry->d_inode, dentry, mode);
> +out_drop_write:
>  	mnt_drop_write(nd.path.mnt);
>  out_dput:
>  	dput(dentry);
> @@ -2192,7 +2208,11 @@ static long do_rmdir(int dfd, const char
>  	error = mnt_want_write(nd.path.mnt);
>  	if (error)
>  		goto exit3;
> +	error = security_path_rmdir(&nd.path, dentry);
> +	if (error)
> +		goto exit4;
>  	error = vfs_rmdir(nd.path.dentry->d_inode, dentry);
> +exit4:
>  	mnt_drop_write(nd.path.mnt);
>  exit3:
>  	dput(dentry);
> @@ -2274,7 +2294,11 @@ static long do_unlinkat(int dfd, const c
>  		error = mnt_want_write(nd.path.mnt);
>  		if (error)
>  			goto exit2;
> +		error = security_path_unlink(&nd.path, dentry);
> +		if (error)
> +			goto exit3;
>  		error = vfs_unlink(nd.path.dentry->d_inode, dentry);
> +exit3:
>  		mnt_drop_write(nd.path.mnt);
>  	exit2:
>  		dput(dentry);
> @@ -2355,7 +2379,11 @@ asmlinkage long sys_symlinkat(const char
>  	error = mnt_want_write(nd.path.mnt);
>  	if (error)
>  		goto out_dput;
> +	error = security_path_symlink(&nd.path, dentry, from);
> +	if (error)
> +		goto out_drop_write;
>  	error = vfs_symlink(nd.path.dentry->d_inode, dentry, from);
> +out_drop_write:
>  	mnt_drop_write(nd.path.mnt);
>  out_dput:
>  	dput(dentry);
> @@ -2452,7 +2480,11 @@ asmlinkage long sys_linkat(int olddfd, c
>  	error = mnt_want_write(nd.path.mnt);
>  	if (error)
>  		goto out_dput;
> +	error = security_path_link(old_path.dentry, &nd.path, new_dentry);
> +	if (error)
> +		goto out_drop_write;
>  	error = vfs_link(old_path.dentry, nd.path.dentry->d_inode, new_dentry);
> +out_drop_write:
>  	mnt_drop_write(nd.path.mnt);
>  out_dput:
>  	dput(new_dentry);
> @@ -2682,8 +2714,13 @@ asmlinkage long sys_renameat(int olddfd,
>  	error = mnt_want_write(oldnd.path.mnt);
>  	if (error)
>  		goto exit5;
> +	error = security_path_rename(&oldnd.path, old_dentry,
> +				     &newnd.path, new_dentry);
> +	if (error)
> +		goto exit6;
>  	error = vfs_rename(old_dir->d_inode, old_dentry,
>  				   new_dir->d_inode, new_dentry);
> +exit6:
>  	mnt_drop_write(oldnd.path.mnt);
>  exit5:
>  	dput(new_dentry);
> --- linux-2.6.27-rc7-mm1.orig/fs/open.c
> +++ linux-2.6.27-rc7-mm1/fs/open.c
> @@ -272,6 +272,8 @@ static long do_sys_truncate(const char _
>  		goto put_write_and_out;
> 
>  	error = locks_verify_truncate(inode, NULL, length);
> +	if (!error)
> +		error = security_path_truncate(&path, length, 0, NULL);
>  	if (!error) {
>  		DQUOT_INIT(inode);
>  		error = do_truncate(path.dentry, length, 0, NULL);
> @@ -329,6 +331,9 @@ static long do_sys_ftruncate(unsigned in
> 
>  	error = locks_verify_truncate(inode, file, length);
>  	if (!error)
> +		error = security_path_truncate(&file->f_path, length,
> +					       ATTR_MTIME|ATTR_CTIME, file);
> +	if (!error)
>  		error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, file);
>  out_putf:
>  	fput(file);
> --- linux-2.6.27-rc7-mm1.orig/include/linux/security.h
> +++ linux-2.6.27-rc7-mm1/include/linux/security.h
> @@ -331,17 +331,37 @@ static inline void security_free_mnt_opt
>   *	@dir contains the inode structure of the parent directory of the new link.
>   *	@new_dentry contains the dentry structure for the new link.
>   *	Return 0 if permission is granted.
> + * @path_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.
> + *	@new_dir contains the path structure of the parent directory of
> + *	the new link.
> + *	@new_dentry contains the dentry structure for the new link.
> + *	Return 0 if permission is granted.
>   * @inode_unlink:
>   *	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.
>   *	Return 0 if permission is granted.
> + * @path_unlink:
> + *	Check the permission to remove a hard link to a file.
> + *	@dir contains the path structure of parent directory of the file.
> + *	@dentry contains the dentry structure for file to be unlinked.
> + *	Return 0 if permission is granted.
>   * @inode_symlink:
>   *	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.
>   *	@old_name contains the pathname of file.
>   *	Return 0 if permission is granted.
> + * @path_symlink:
> + *	Check the permission to create a symbolic link to a file.
> + *	@dir contains the path structure of parent directory of
> + *	the symbolic link.
> + *	@dentry contains the dentry structure of the symbolic link.
> + *	@old_name contains the pathname of file.
> + *	Return 0 if permission is granted.
>   * @inode_mkdir:
>   *	Check permissions to create a new directory in the existing directory
>   *	associated with inode strcture @dir.
> @@ -349,11 +369,25 @@ static inline void security_free_mnt_opt
>   *	@dentry contains the dentry structure of new directory.
>   *	@mode contains the mode of new directory.
>   *	Return 0 if permission is granted.
> + * @path_mkdir:
> + *	Check permissions to create a new directory in the existing directory
> + *	associated with path strcture @path.
> + *	@dir containst the path structure of parent of the directory
> + *	to be created.
> + *	@dentry contains the dentry structure of new directory.
> + *	@mode contains the mode of new directory.
> + *	Return 0 if permission is granted.
>   * @inode_rmdir:
>   *	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.
>   *	Return 0 if permission is granted.
> + * @path_rmdir:
> + *	Check the permission to remove a directory.
> + *	@dir contains the path structure of parent of the directory to be
> + *	removed.
> + *	@dentry contains the dentry structure of directory to be removed.
> + *	Return 0 if permission is granted.
>   * @inode_mknod:
>   *	Check permissions when creating a special file (or a socket or a fifo
>   *	file created via the mknod system call).  Note that if mknod operation
> @@ -364,6 +398,15 @@ static inline void security_free_mnt_opt
>   *	@mode contains the mode of the new file.
>   *	@dev contains the device number.
>   *	Return 0 if permission is granted.
> + * @path_mknod:
> + *	Check permissions when creating a file. Note that this hook is called
> + *	even if mknod operation is being done for a regular file.
> + *	@dir contains the path structure of parent of the new file.
> + *	@dentry contains the dentry structure of the new file.
> + *	@mode contains the mode of the new file.
> + *	@dev contains the undecoded device number. Use new_decode_dev() to get
> + *	the decoded device number.
> + *	Return 0 if permission is granted.
>   * @inode_rename:
>   *	Check for permission to rename a file or directory.
>   *	@old_dir contains the inode structure for parent of the old link.
> @@ -371,6 +414,13 @@ static inline void security_free_mnt_opt
>   *	@new_dir contains the inode structure for parent of the new link.
>   *	@new_dentry contains the dentry structure of the new link.
>   *	Return 0 if permission is granted.
> + * @path_rename:
> + *	Check for permission to rename a file or directory.
> + *	@old_dir contains the path structure for parent of the old link.
> + *	@old_dentry contains the dentry structure of the old link.
> + *	@new_dir contains the path structure for parent of the new link.
> + *	@new_dentry contains the dentry structure of the new link.
> + *	Return 0 if permission is granted.
>   * @inode_readlink:
>   *	Check the permission to read the symbolic link.
>   *	@dentry contains the dentry structure for the file link.
> @@ -399,6 +449,13 @@ static inline void security_free_mnt_opt
>   *	@dentry contains the dentry structure for the file.
>   *	@attr is the iattr structure containing the new file attributes.
>   *	Return 0 if permission is granted.
> + * @path_truncate:
> + *	Check permission before truncating a file.
> + *	@path contains the path structure for the file.
> + *	@length is the new length of the file.
> + *	@time_attrs is the flags passed to do_truncate().
> + *	@filp is the file structure (may be NULL).
> + *	Return 0 if permission is granted.
>   * @inode_getattr:
>   *	Check permission before obtaining file attributes.
>   *	@mnt is the vfsmount where the dentry was looked up
> @@ -1327,6 +1384,22 @@ struct security_operations {
>  				   struct super_block *newsb);
>  	int (*sb_parse_opts_str) (char *options, struct security_mnt_opts *opts);
> 
> +#ifdef CONFIG_SECURITY_PATH
> +	int (*path_unlink) (struct path *dir, struct dentry *dentry);
> +	int (*path_mkdir) (struct path *dir, struct dentry *dentry, int mode);
> +	int (*path_rmdir) (struct path *dir, struct dentry *dentry);
> +	int (*path_mknod) (struct path *dir, struct dentry *dentry, int mode,
> +			   unsigned int dev);
> +	int (*path_truncate) (struct path *path, loff_t length,
> +			      unsigned int time_attrs, struct file *filp);
> +	int (*path_symlink) (struct path *dir, struct dentry *dentry,
> +			     const char *old_name);
> +	int (*path_link) (struct dentry *old_dentry, struct path *new_dir,
> +			  struct dentry *new_dentry);
> +	int (*path_rename) (struct path *old_dir, struct dentry *old_dentry,
> +			    struct path *new_dir, struct dentry *new_dentry);
> +#endif
> +
>  	int (*inode_alloc_security) (struct inode *inode);
>  	void (*inode_free_security) (struct inode *inode);
>  	int (*inode_init_security) (struct inode *inode, struct inode *dir,
> @@ -2685,6 +2758,72 @@ static inline void security_skb_classify
> 
>  #endif	/* CONFIG_SECURITY_NETWORK_XFRM */
> 
> +#ifdef CONFIG_SECURITY_PATH
> +int security_path_unlink(struct path *dir, struct dentry *dentry);
> +int security_path_mkdir(struct path *dir, struct dentry *dentry, int mode);
> +int security_path_rmdir(struct path *dir, struct dentry *dentry);
> +int security_path_mknod(struct path *dir, struct dentry *dentry, int mode,
> +			unsigned int dev);
> +int security_path_truncate(struct path *path, loff_t length,
> +			   unsigned int time_attrs, struct file *filp);
> +int security_path_symlink(struct path *dir, struct dentry *dentry,
> +			  const char *old_name);
> +int security_path_link(struct dentry *old_dentry, struct path *new_dir,
> +		       struct dentry *new_dentry);
> +int security_path_rename(struct path *old_dir, struct dentry *old_dentry,
> +			 struct path *new_dir, struct dentry *new_dentry);
> +#else	/* CONFIG_SECURITY_PATH */
> +static inline int security_path_unlink(struct path *dir, struct dentry *dentry)
> +{
> +	return 0;
> +}
> +
> +static inline int security_path_mkdir(struct path *dir, struct dentry *dentry,
> +				      int mode)
> +{
> +	return 0;
> +}
> +
> +static inline int security_path_rmdir(struct path *dir, struct dentry *dentry)
> +{
> +	return 0;
> +}
> +
> +static inline int security_path_mknod(struct path *dir, struct dentry *dentry,
> +				      int mode, unsigned int dev)
> +{
> +	return 0;
> +}
> +
> +static inline int security_path_truncate(struct path *path, loff_t length,
> +					 unsigned int time_attrs,
> +					 struct file *filp)
> +{
> +	return 0;
> +}
> +
> +static inline int security_path_symlink(struct path *dir, struct dentry *dentry,
> +					const char *old_name)
> +{
> +	return 0;
> +}
> +
> +static inline int security_path_link(struct dentry *old_dentry,
> +				     struct path *new_dir,
> +				     struct dentry *new_dentry)
> +{
> +	return 0;
> +}
> +
> +static inline int security_path_rename(struct path *old_dir,
> +				       struct dentry *old_dentry,
> +				       struct path *new_dir,
> +				       struct dentry *new_dentry)
> +{
> +	return 0;
> +}
> +#endif	/* CONFIG_SECURITY_PATH */
> +
>  #ifdef CONFIG_KEYS
>  #ifdef CONFIG_SECURITY
> 
> --- linux-2.6.27-rc7-mm1.orig/net/unix/af_unix.c
> +++ linux-2.6.27-rc7-mm1/net/unix/af_unix.c
> @@ -828,7 +828,11 @@ static int unix_bind(struct socket *sock
>  		err = mnt_want_write(nd.path.mnt);
>  		if (err)
>  			goto out_mknod_dput;
> +		err = security_path_mknod(&nd.path, dentry, mode, 0);
> +		if (err)
> +			goto out_mknod_drop_write;
>  		err = vfs_mknod(nd.path.dentry->d_inode, dentry, mode, 0);
> +out_mknod_drop_write:
>  		mnt_drop_write(nd.path.mnt);
>  		if (err)
>  			goto out_mknod_dput;
> --- linux-2.6.27-rc7-mm1.orig/security/capability.c
> +++ linux-2.6.27-rc7-mm1/security/capability.c
> @@ -263,6 +263,53 @@ static void cap_inode_getsecid(const str
>  	*secid = 0;
>  }
> 
> +#ifdef CONFIG_SECURITY_PATH
> +static int cap_path_mknod(struct path *dir, struct dentry *dentry, int mode,
> +			  unsigned int dev)
> +{
> +	return 0;
> +}
> +
> +static int cap_path_mkdir(struct path *dir, struct dentry *dentry, int mode)
> +{
> +	return 0;
> +}
> +
> +static int cap_path_rmdir(struct path *dir, struct dentry *dentry)
> +{
> +	return 0;
> +}
> +
> +static int cap_path_unlink(struct path *dir, struct dentry *dentry)
> +{
> +	return 0;
> +}
> +
> +static int cap_path_symlink(struct path *dir, struct dentry *dentry,
> +			    const char *old_name)
> +{
> +	return 0;
> +}
> +
> +static int cap_path_link(struct dentry *old_dentry, struct path *new_dir,
> +			 struct dentry *new_dentry)
> +{
> +	return 0;
> +}
> +
> +static int cap_path_rename(struct path *old_path, struct dentry *old_dentry,
> +			   struct path *new_path, struct dentry *new_dentry)
> +{
> +	return 0;
> +}
> +
> +static int cap_path_truncate(struct path *path, loff_t length,
> +			     unsigned int time_attrs, struct file *filp)
> +{
> +	return 0;
> +}
> +#endif
> +
>  static int cap_file_permission(struct file *file, int mask)
>  {
>  	return 0;
> @@ -883,6 +930,16 @@ void security_fixup_ops(struct security_
>  	set_to_cap_if_null(ops, inode_setsecurity);
>  	set_to_cap_if_null(ops, inode_listsecurity);
>  	set_to_cap_if_null(ops, inode_getsecid);
> +#ifdef CONFIG_SECURITY_PATH
> +	set_to_cap_if_null(ops, path_mknod);
> +	set_to_cap_if_null(ops, path_mkdir);
> +	set_to_cap_if_null(ops, path_rmdir);
> +	set_to_cap_if_null(ops, path_unlink);
> +	set_to_cap_if_null(ops, path_symlink);
> +	set_to_cap_if_null(ops, path_link);
> +	set_to_cap_if_null(ops, path_rename);
> +	set_to_cap_if_null(ops, path_truncate);
> +#endif
>  	set_to_cap_if_null(ops, file_permission);
>  	set_to_cap_if_null(ops, file_alloc_security);
>  	set_to_cap_if_null(ops, file_free_security);
> --- linux-2.6.27-rc7-mm1.orig/security/security.c
> +++ linux-2.6.27-rc7-mm1/security/security.c
> @@ -16,6 +16,8 @@
>  #include <linux/init.h>
>  #include <linux/kernel.h>
>  #include <linux/security.h>
> +#include <linux/audit.h>
> +#include <linux/device_cgroup.h>
> 
>  /* Boot-time LSM user choice */
>  static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1];
> @@ -341,6 +343,234 @@ int security_inode_init_security(struct 
>  }
>  EXPORT_SYMBOL(security_inode_init_security);
> 
> +#ifdef CONFIG_SECURITY_PATH
> +
> +/* Copied from fs/namei.c */
> +static inline int may_create(struct inode *dir, struct dentry *child)
> +{
> +	if (child->d_inode)
> +		return -EEXIST;
> +	if (IS_DEADDIR(dir))
> +		return -ENOENT;
> +	return inode_permission(dir, MAY_WRITE | MAY_EXEC);
> +}
> +
> +/* Copied from fs/namei.c */
> +static inline int check_sticky(struct inode *dir, struct inode *inode)
> +{
> +	uid_t fsuid = current_fsuid();
> +
> +	if (!(dir->i_mode & S_ISVTX))
> +		return 0;
> +	if (inode->i_uid == fsuid)
> +		return 0;
> +	if (dir->i_uid == fsuid)
> +		return 0;
> +	return !capable(CAP_FOWNER);
> +}
> +
> +/* Copied from fs/namei.c */
> +static int may_delete(struct inode *dir, struct dentry *victim, int isdir)
> +{
> +	int error;
> +
> +	if (!victim->d_inode)
> +		return -ENOENT;
> +
> +	BUG_ON(victim->d_parent->d_inode != dir);
> +	audit_inode_child(victim->d_name.name, victim, dir);
> +
> +	error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
> +	if (error)
> +		return error;
> +	if (IS_APPEND(dir))
> +		return -EPERM;
> +	if (check_sticky(dir, victim->d_inode) || IS_APPEND(victim->d_inode) ||
> +	    IS_IMMUTABLE(victim->d_inode))
> +		return -EPERM;
> +	if (isdir) {
> +		if (!S_ISDIR(victim->d_inode->i_mode))
> +			return -ENOTDIR;
> +		if (IS_ROOT(victim))
> +			return -EBUSY;
> +	} else if (S_ISDIR(victim->d_inode->i_mode))
> +		return -EISDIR;
> +	if (IS_DEADDIR(dir))
> +		return -ENOENT;
> +	if (victim->d_flags & DCACHE_NFSFS_RENAMED)
> +		return -EBUSY;
> +	return 0;
> +}
> +
> +int security_path_mknod(struct path *path, struct dentry *dentry, int mode,
> +			unsigned int dev)
> +{
> +	/* may_mknod() checked mode is valid. */
> +	struct inode *dir = path->dentry->d_inode;
> +	int error = may_create(dir, dentry);
> +	if (error)
> +		return error;
> +	switch (mode & S_IFMT) {
> +	case S_IFREG:
> +	case 0:
> +		if (!dir->i_op || !dir->i_op->create)
> +			return -EACCES; /* shouldn't it be ENOSYS? */
> +		mode &= S_IALLUGO;
> +		mode |= S_IFREG;
> +		break;
> +	case S_IFCHR:
> +	case S_IFBLK:
> +	case S_IFIFO:
> +	case S_IFSOCK:
> +		if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD))
> +			return -EPERM;
> +		if (!dir->i_op || !dir->i_op->mknod)
> +			return -EPERM;
> +		error = devcgroup_inode_mknod(mode, dev);
> +		if (error)
> +			return error;
> +		break;
> +	}
> +	if (unlikely(IS_PRIVATE(dir)))
> +		return 0;
> +	return security_ops->path_mknod(path, dentry, mode, dev);
> +}
> +EXPORT_SYMBOL(security_path_mknod);
> +
> +int security_path_mkdir(struct path *path, struct dentry *dentry, int mode)
> +{
> +	struct inode *dir = path->dentry->d_inode;
> +	int error = may_create(dir, dentry);
> +	if (error)
> +		return error;
> +	if (!dir->i_op || !dir->i_op->mkdir)
> +		return -EPERM;
> +	if (unlikely(IS_PRIVATE(dir)))
> +		return 0;
> +	mode &= (S_IRWXUGO | S_ISVTX);
> +	return security_ops->path_mkdir(path, dentry, mode);
> +}
> +
> +int security_path_rmdir(struct path *path, struct dentry *dentry)
> +{
> +	struct inode *dir = path->dentry->d_inode;
> +	int error = may_delete(dir, dentry, 1);
> +	if (error)
> +		return error;
> +	if (!dir->i_op || !dir->i_op->rmdir)
> +		return -EPERM;
> +	mutex_lock(&dentry->d_inode->i_mutex);
> +	/* I don't call dentry_unhash() here. */
> +	if (likely(!IS_PRIVATE(dir)))
> +		error = security_ops->path_rmdir(path, dentry);
> +	mutex_unlock(&dentry->d_inode->i_mutex);
> +	return error;
> +}
> +
> +int security_path_unlink(struct path *path, struct dentry *dentry)
> +{
> +	struct inode *dir = path->dentry->d_inode;
> +	int error = may_delete(dir, dentry, 0);
> +	if (error)
> +		return error;
> +	if (!dir->i_op || !dir->i_op->unlink)
> +		return -EPERM;
> +	mutex_lock(&dentry->d_inode->i_mutex);
> +	if (likely(!IS_PRIVATE(dir)))
> +		error = security_ops->path_unlink(path, dentry);
> +	mutex_unlock(&dentry->d_inode->i_mutex);
> +	return error;
> +}
> +
> +int security_path_symlink(struct path *path, struct dentry *dentry,
> +			  const char *old_name)
> +{
> +	struct inode *dir = path->dentry->d_inode;
> +	int error = may_create(dir, dentry);
> +	if (error)
> +		return error;
> +	if (!dir->i_op || !dir->i_op->symlink)
> +		return -EPERM;
> +	if (unlikely(IS_PRIVATE(dir)))
> +		return 0;
> +	return security_ops->path_symlink(path, dentry, old_name);
> +}
> +
> +int security_path_link(struct dentry *old_dentry, struct path *new_dir,
> +		       struct dentry *new_dentry)
> +{
> +	struct inode *dir = new_dir->dentry->d_inode;
> +	struct inode *inode = old_dentry->d_inode;
> +	int error;
> +	if (!inode)
> +		return -ENOENT;
> +	error = may_create(dir, new_dentry);
> +	if (error)
> +		return error;
> +	if (dir->i_sb != inode->i_sb)
> +		return -EXDEV;
> +	if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
> +		return -EPERM;
> +	if (!dir->i_op || !dir->i_op->link)
> +		return -EPERM;
> +	if (S_ISDIR(inode->i_mode))
> +		return -EPERM;
> +	if (unlikely(IS_PRIVATE(inode)))
> +		return 0;
> +	return security_ops->path_link(old_dentry, new_dir, new_dentry);
> +}
> +
> +int security_path_rename(struct path *old_path, struct dentry *old_dentry,
> +			 struct path *new_path, struct dentry *new_dentry)
> +{
> +	struct inode *old_dir = old_path->dentry->d_inode;
> +	struct inode *new_dir = new_path->dentry->d_inode;
> +
> +	int error;
> +	int is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
> +	if (old_dentry->d_inode == new_dentry->d_inode)
> +		return 0;
> +	error = may_delete(old_dir, old_dentry, is_dir);
> +	if (error)
> +		return error;
> +	if (!new_dentry->d_inode)
> +		error = may_create(new_dir, new_dentry);
> +	else
> +		error = may_delete(new_dir, new_dentry, is_dir);
> +	if (error)
> +		return error;
> +	if (!old_dir->i_op || !old_dir->i_op->rename)
> +		return -EPERM;
> +
> +	/* I don't call fsnotify_oldname_init() here. */
> +
> +	if (is_dir && new_dir != old_dir) {
> +		error = inode_permission(old_dentry->d_inode, MAY_WRITE);
> +		if (error)
> +			return error;
> +	}
> +	if (unlikely(IS_PRIVATE(old_dentry->d_inode) ||
> +		     (new_dentry->d_inode && IS_PRIVATE(new_dentry->d_inode))))
> +		return 0;
> +	return security_ops->path_rename(old_path, old_dentry, new_path,
> +					 new_dentry);
> +}
> +
> +int security_path_truncate(struct path *path, loff_t length,
> +			   unsigned int time_attrs, struct file *filp)
> +{
> +	struct dentry *dentry = path->dentry;
> +	int error = 0;
> +	mutex_lock(&dentry->d_inode->i_mutex);
> +	if (likely(!IS_PRIVATE(dentry->d_inode)))
> +		error = security_ops->path_truncate(path, length, time_attrs,
> +						    filp);
> +	mutex_unlock(&dentry->d_inode->i_mutex);
> +	return error;
> +}
> +
> +#endif
> +
>  int security_inode_create(struct inode *dir, struct dentry *dentry, int mode)
>  {
>  	if (unlikely(IS_PRIVATE(dir)))
> --- linux-2.6.27-rc7-mm1.orig/security/Kconfig
> +++ linux-2.6.27-rc7-mm1/security/Kconfig
> @@ -81,6 +81,15 @@ config SECURITY_NETWORK_XFRM
>  	  IPSec.
>  	  If you are unsure how to answer this question, answer N.
> 
> +config SECURITY_PATH
> +	bool "Security hooks for pathname based access control"
> +	depends on SECURITY
> +	help
> +	  This enables the security hooks for pathname based access control.
> +	  If enabled, a security module can use these hooks to
> +	  implement pathname based access controls.
> +	  If you are unsure how to answer this question, answer N.
> +
>  config SECURITY_FILE_CAPABILITIES
>  	bool "File POSIX Capabilities"
>  	default n
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ