]> Pileus Git - ~andy/linux/blobdiff - fs/namei.c
fs: prevent use after free in auditing when symlink following was denied
[~andy/linux] / fs / namei.c
index 1b464390dde85ecd783eb45f64aee1f89f6c3ff1..6d47fac64292e0155ef925ef78be737f4bf826ab 100644 (file)
@@ -352,6 +352,7 @@ int __inode_permission(struct inode *inode, int mask)
 /**
  * sb_permission - Check superblock-level permissions
  * @sb: Superblock of inode to check permission on
+ * @inode: Inode to check permission on
  * @mask: Right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
  *
  * Separate out file-system wide checks from inode-specific permission checks.
@@ -656,6 +657,7 @@ int sysctl_protected_hardlinks __read_mostly = 1;
 /**
  * may_follow_link - Check symlink following for unsafe situations
  * @link: The path of the symlink
+ * @nd: nameidata pathwalk data
  *
  * In the case of the sysctl_protected_symlinks sysctl being enabled,
  * CAP_DAC_OVERRIDE needs to be specifically ignored if the symlink is
@@ -678,7 +680,7 @@ static inline int may_follow_link(struct path *link, struct nameidata *nd)
 
        /* Allowed if owner and follower match. */
        inode = link->dentry->d_inode;
-       if (current_cred()->fsuid == inode->i_uid)
+       if (uid_eq(current_cred()->fsuid, inode->i_uid))
                return 0;
 
        /* Allowed if parent directory not sticky and world-writable. */
@@ -687,12 +689,12 @@ static inline int may_follow_link(struct path *link, struct nameidata *nd)
                return 0;
 
        /* Allowed if parent directory and link owner match. */
-       if (parent->i_uid == inode->i_uid)
+       if (uid_eq(parent->i_uid, inode->i_uid))
                return 0;
 
+       audit_log_link_denied("follow_link", link);
        path_put_conditional(link, nd);
        path_put(&nd->path);
-       audit_log_link_denied("follow_link", link);
        return -EACCES;
 }
 
@@ -757,7 +759,7 @@ static int may_linkat(struct path *link)
        /* Source inode owner (or CAP_FOWNER) can hardlink all they like,
         * otherwise, it must be a safe source.
         */
-       if (cred->fsuid == inode->i_uid || safe_hardlink_source(inode) ||
+       if (uid_eq(cred->fsuid, inode->i_uid) || safe_hardlink_source(inode) ||
            capable(CAP_FOWNER))
                return 0;
 
@@ -1795,8 +1797,6 @@ static int path_init(int dfd, const char *name, unsigned int flags,
                     struct nameidata *nd, struct file **fp)
 {
        int retval = 0;
-       int fput_needed;
-       struct file *file;
 
        nd->last_type = LAST_ROOT; /* if there are only slashes... */
        nd->flags = flags | LOOKUP_JUMPED;
@@ -1848,44 +1848,41 @@ static int path_init(int dfd, const char *name, unsigned int flags,
                        get_fs_pwd(current->fs, &nd->path);
                }
        } else {
+               struct fd f = fdget_raw(dfd);
                struct dentry *dentry;
 
-               file = fget_raw_light(dfd, &fput_needed);
-               retval = -EBADF;
-               if (!file)
-                       goto out_fail;
+               if (!f.file)
+                       return -EBADF;
 
-               dentry = file->f_path.dentry;
+               dentry = f.file->f_path.dentry;
 
                if (*name) {
-                       retval = -ENOTDIR;
-                       if (!S_ISDIR(dentry->d_inode->i_mode))
-                               goto fput_fail;
+                       if (!S_ISDIR(dentry->d_inode->i_mode)) {
+                               fdput(f);
+                               return -ENOTDIR;
+                       }
 
                        retval = inode_permission(dentry->d_inode, MAY_EXEC);
-                       if (retval)
-                               goto fput_fail;
+                       if (retval) {
+                               fdput(f);
+                               return retval;
+                       }
                }
 
-               nd->path = file->f_path;
+               nd->path = f.file->f_path;
                if (flags & LOOKUP_RCU) {
-                       if (fput_needed)
-                               *fp = file;
+                       if (f.need_put)
+                               *fp = f.file;
                        nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq);
                        lock_rcu_walk();
                } else {
-                       path_get(&file->f_path);
-                       fput_light(file, fput_needed);
+                       path_get(&nd->path);
+                       fdput(f);
                }
        }
 
        nd->inode = nd->path.dentry->d_inode;
        return 0;
-
-fput_fail:
-       fput_light(file, fput_needed);
-out_fail:
-       return retval;
 }
 
 static inline int lookup_last(struct nameidata *nd, struct path *path)
@@ -2414,7 +2411,7 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
                goto out;
        }
 
-       mode = op->mode & S_IALLUGO;
+       mode = op->mode;
        if ((open_flag & O_CREAT) && !IS_POSIXACL(dir))
                mode &= ~current_umask();
 
@@ -2452,7 +2449,7 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
        }
 
        if (open_flag & O_CREAT) {
-               error = may_o_create(&nd->path, dentry, op->mode);
+               error = may_o_create(&nd->path, dentry, mode);
                if (error) {
                        create_error = error;
                        if (open_flag & O_EXCL)
@@ -2489,6 +2486,10 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
                        dput(dentry);
                        dentry = file->f_path.dentry;
                }
+               if (create_error && dentry->d_inode == NULL) {
+                       error = create_error;
+                       goto out;
+               }
                goto looked_up;
        }
 
@@ -3965,7 +3966,7 @@ EXPORT_SYMBOL(user_path_at);
 EXPORT_SYMBOL(follow_down_one);
 EXPORT_SYMBOL(follow_down);
 EXPORT_SYMBOL(follow_up);
-EXPORT_SYMBOL(get_write_access); /* binfmt_aout */
+EXPORT_SYMBOL(get_write_access); /* nfsd */
 EXPORT_SYMBOL(getname);
 EXPORT_SYMBOL(lock_rename);
 EXPORT_SYMBOL(lookup_one_len);