introduce new LSM hooks where vfsmount is available.
Kentaro Takeda [Wed, 17 Dec 2008 04:24:15 +0000 (13:24 +0900)]
Add new LSM hooks for path-based checks.  Call them on directory-modifying
operations at the points where we still know the vfsmount involved.

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>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

fs/namei.c
fs/open.c
include/linux/security.h
net/unix/af_unix.c
security/Kconfig
security/capability.c
security/security.c

index af3783f..ab441af 100644 (file)
@@ -1556,6 +1556,9 @@ int may_open(struct nameidata *nd, int acc_mode, int flag)
                 * 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);
                if (!error) {
                        DQUOT_INIT(inode);
 
@@ -1586,7 +1589,11 @@ static int __open_namei_create(struct nameidata *nd, struct path *path,
 
        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;
@@ -1999,6 +2006,9 @@ asmlinkage long sys_mknodat(int dfd, const char __user *filename, int mode,
        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);
@@ -2011,6 +2021,7 @@ asmlinkage long sys_mknodat(int dfd, const char __user *filename, int mode,
                        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);
@@ -2070,7 +2081,11 @@ asmlinkage long sys_mkdirat(int dfd, const char __user *pathname, int mode)
        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);
@@ -2180,7 +2195,11 @@ static long do_rmdir(int dfd, const char __user *pathname)
        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);
@@ -2265,7 +2284,11 @@ static long do_unlinkat(int dfd, const char __user *pathname)
                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);
@@ -2346,7 +2369,11 @@ asmlinkage long sys_symlinkat(const char __user *oldname,
        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);
@@ -2443,7 +2470,11 @@ asmlinkage long sys_linkat(int olddfd, const char __user *oldname,
        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);
@@ -2679,8 +2710,13 @@ asmlinkage long sys_renameat(int olddfd, const char __user *oldname,
        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);
index c0a426d..1cd7d40 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -272,6 +272,8 @@ static long do_sys_truncate(const char __user *pathname, loff_t length)
                goto put_write_and_out;
 
        error = locks_verify_truncate(inode, NULL, length);
+       if (!error)
+               error = security_path_truncate(&path, length, 0);
        if (!error) {
                DQUOT_INIT(inode);
                error = do_truncate(path.dentry, length, 0, NULL);
@@ -329,6 +331,9 @@ static long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
 
        error = locks_verify_truncate(inode, file, length);
        if (!error)
+               error = security_path_truncate(&file->f_path, length,
+                                              ATTR_MTIME|ATTR_CTIME);
+       if (!error)
                error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, file);
 out_putf:
        fput(file);
index 3416cb8..b92b5e4 100644 (file)
@@ -335,17 +335,37 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *     @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.
@@ -353,11 +373,25 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *     @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
@@ -368,6 +402,15 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *     @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.
@@ -375,6 +418,13 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *     @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.
@@ -403,6 +453,12 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *     @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().
+ *     Return 0 if permission is granted.
  * @inode_getattr:
  *     Check permission before obtaining file attributes.
  *     @mnt is the vfsmount where the dentry was looked up
@@ -1331,6 +1387,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);
+       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,
@@ -2705,6 +2777,71 @@ static inline void security_skb_classify_flow(struct sk_buff *skb, struct flowi
 
 #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);
+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)
+{
+       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
 
index c6250d0..d1b8982 100644 (file)
@@ -836,7 +836,11 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
                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;
index d9f47ce..9438535 100644 (file)
@@ -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
index 2dce66f..c545bd1 100644 (file)
@@ -263,6 +263,53 @@ static void cap_inode_getsecid(const struct inode *inode, u32 *secid)
        *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)
+{
+       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_operations *ops)
        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);
index d85dbb3..678d4d0 100644 (file)
@@ -355,6 +355,72 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
 }
 EXPORT_SYMBOL(security_inode_init_security);
 
+#ifdef CONFIG_SECURITY_PATH
+int security_path_mknod(struct path *path, struct dentry *dentry, int mode,
+                       unsigned int dev)
+{
+       if (unlikely(IS_PRIVATE(path->dentry->d_inode)))
+               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)
+{
+       if (unlikely(IS_PRIVATE(path->dentry->d_inode)))
+               return 0;
+       return security_ops->path_mkdir(path, dentry, mode);
+}
+
+int security_path_rmdir(struct path *path, struct dentry *dentry)
+{
+       if (unlikely(IS_PRIVATE(path->dentry->d_inode)))
+               return 0;
+       return security_ops->path_rmdir(path, dentry);
+}
+
+int security_path_unlink(struct path *path, struct dentry *dentry)
+{
+       if (unlikely(IS_PRIVATE(path->dentry->d_inode)))
+               return 0;
+       return security_ops->path_unlink(path, dentry);
+}
+
+int security_path_symlink(struct path *path, struct dentry *dentry,
+                         const char *old_name)
+{
+       if (unlikely(IS_PRIVATE(path->dentry->d_inode)))
+               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)
+{
+       if (unlikely(IS_PRIVATE(old_dentry->d_inode)))
+               return 0;
+       return security_ops->path_link(old_dentry, new_dir, new_dentry);
+}
+
+int security_path_rename(struct path *old_dir, struct dentry *old_dentry,
+                        struct path *new_dir, struct dentry *new_dentry)
+{
+       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_dir, old_dentry, new_dir,
+                                        new_dentry);
+}
+
+int security_path_truncate(struct path *path, loff_t length,
+                          unsigned int time_attrs)
+{
+       if (unlikely(IS_PRIVATE(path->dentry->d_inode)))
+               return 0;
+       return security_ops->path_truncate(path, length, time_attrs);
+}
+#endif
+
 int security_inode_create(struct inode *dir, struct dentry *dentry, int mode)
 {
        if (unlikely(IS_PRIVATE(dir)))