Messages in this thread | | | Date | Tue, 30 Sep 2008 10:45:53 -0500 | From | "Serge E. Hallyn" <> | Subject | Re: [TOMOYO #9 (2.6.27-rc7-mm1) 1/6] LSM adapter functions. |
| |
Quoting Kentaro Takeda (takedakn@nttdata.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@nttdata.co.jp> > Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> > Signed-off-by: Toshiharu Harada <haradats@nttdata.co.jp> > Cc: Al Viro <viro@zeniv.linux.org.uk> > Cc: Christoph Hellwig <hch@lst.de> > Cc: Crispin Cowan <crispin@crispincowan.com> > Cc: Stephen Smalley <sds@tycho.nsa.gov> > Cc: Casey Schaufler <casey@schaufler-ca.com> > Signed-off-by: Andrew Morton <akpm@linux-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 >
| |