kernel: Only expose su when daemon is running
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / fs / namei.c
index 9ed9361223c08f30ebbcca48e165588adeb31378..36d4b29459ecb52a713c1cc3565e426a36e43c77 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/device_cgroup.h>
 #include <linux/fs_struct.h>
 #include <linux/posix_acl.h>
+#include <linux/hash.h>
 #include <asm/uaccess.h>
 
 #include "internal.h"
@@ -321,10 +322,11 @@ int generic_permission(struct inode *inode, int mask)
 
        if (S_ISDIR(inode->i_mode)) {
                /* DACs are overridable for directories */
-               if (inode_capable(inode, CAP_DAC_OVERRIDE))
+               if (capable_wrt_inode_uidgid(inode, CAP_DAC_OVERRIDE))
                        return 0;
                if (!(mask & MAY_WRITE))
-                       if (inode_capable(inode, CAP_DAC_READ_SEARCH))
+                       if (capable_wrt_inode_uidgid(inode,
+                                                    CAP_DAC_READ_SEARCH))
                                return 0;
                return -EACCES;
        }
@@ -334,7 +336,7 @@ int generic_permission(struct inode *inode, int mask)
         * at least one exec bit set.
         */
        if (!(mask & MAY_EXEC) || (inode->i_mode & S_IXUGO))
-               if (inode_capable(inode, CAP_DAC_OVERRIDE))
+               if (capable_wrt_inode_uidgid(inode, CAP_DAC_OVERRIDE))
                        return 0;
 
        /*
@@ -342,7 +344,7 @@ int generic_permission(struct inode *inode, int mask)
         */
        mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
        if (mask == MAY_READ)
-               if (inode_capable(inode, CAP_DAC_READ_SEARCH))
+               if (capable_wrt_inode_uidgid(inode, CAP_DAC_READ_SEARCH))
                        return 0;
 
        return -EACCES;
@@ -471,6 +473,24 @@ void path_put(const struct path *path)
 }
 EXPORT_SYMBOL(path_put);
 
+/**
+ * path_connected - Verify that a path->dentry is below path->mnt.mnt_root
+ * @path: nameidate to verify
+ *
+ * Rename can sometimes move a file or directory outside of a bind
+ * mount, path_connected allows those cases to be detected.
+ */
+static bool path_connected(const struct path *path)
+{
+       struct vfsmount *mnt = path->mnt;
+
+       /* Only bind mounts can have disconnected paths */
+       if (mnt->mnt_root == mnt->mnt_sb->s_root)
+               return true;
+
+       return is_subdir(path->dentry, mnt->mnt_root);
+}
+
 /*
  * Path walking has 2 modes, rcu-walk and ref-walk (see
  * Documentation/filesystems/path-lookup.txt).  In situations when we can't
@@ -1146,6 +1166,8 @@ static int follow_dotdot_rcu(struct nameidata *nd)
                                goto failed;
                        nd->path.dentry = parent;
                        nd->seq = seq;
+                       if (unlikely(!path_connected(&nd->path)))
+                               goto failed;
                        break;
                }
                if (!follow_up_rcu(&nd->path))
@@ -1229,7 +1251,7 @@ static void follow_mount(struct path *path)
        }
 }
 
-static void follow_dotdot(struct nameidata *nd)
+static int follow_dotdot(struct nameidata *nd)
 {
        set_root(nd);
 
@@ -1244,6 +1266,10 @@ static void follow_dotdot(struct nameidata *nd)
                        /* rare case of legitimate dget_parent()... */
                        nd->path.dentry = dget_parent(nd->path.dentry);
                        dput(old);
+                       if (unlikely(!path_connected(&nd->path))) {
+                               path_put(&nd->path);
+                               return -ENOENT;
+                       }
                        break;
                }
                if (!follow_up(&nd->path))
@@ -1251,6 +1277,7 @@ static void follow_dotdot(struct nameidata *nd)
        }
        follow_mount(&nd->path);
        nd->inode = nd->path.dentry->d_inode;
+       return 0;
 }
 
 /*
@@ -1474,7 +1501,7 @@ static inline int handle_dots(struct nameidata *nd, int type)
                        if (follow_dotdot_rcu(nd))
                                return -ECHILD;
                } else
-                       follow_dotdot(nd);
+                       return follow_dotdot(nd);
        }
        return 0;
 }
@@ -1540,7 +1567,8 @@ static inline int walk_component(struct nameidata *nd, struct path *path,
 
        if (should_follow_link(inode, follow)) {
                if (nd->flags & LOOKUP_RCU) {
-                       if (unlikely(unlazy_walk(nd, path->dentry))) {
+                       if (unlikely(nd->path.mnt != path->mnt ||
+                                    unlazy_walk(nd, path->dentry))) {
                                err = -ECHILD;
                                goto out_err;
                        }
@@ -1646,8 +1674,7 @@ static inline int can_lookup(struct inode *inode)
 
 static inline unsigned int fold_hash(unsigned long hash)
 {
-       hash += hash >> (8*sizeof(int));
-       return hash;
+       return hash_64(hash, 32);
 }
 
 #else  /* 32-bit case */
@@ -1982,6 +2009,14 @@ static int path_lookupat(int dfd, const char *name,
                }
        }
 
+       if (!err) {
+               struct super_block *sb = nd->inode->i_sb;
+               if (sb->s_flags & MS_RDONLY) {
+                       if (d_is_su(nd->path.dentry) && !su_visible())
+                               err = -ENOENT;
+               }
+       }
+
        if (base)
                fput(base);
 
@@ -2199,7 +2234,7 @@ static inline int check_sticky(struct inode *dir, struct inode *inode)
                return 0;
        if (uid_eq(dir->i_uid, fsuid))
                return 0;
-       return !inode_capable(inode, CAP_FOWNER);
+       return !capable_wrt_inode_uidgid(inode, CAP_FOWNER);
 }
 
 /*
@@ -2263,6 +2298,7 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
  */
 static inline int may_create(struct inode *dir, struct dentry *child)
 {
+       audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE);
        if (child->d_inode)
                return -EEXIST;
        if (IS_DEADDIR(dir))
@@ -2822,7 +2858,8 @@ finish_lookup:
 
        if (should_follow_link(inode, !symlink_ok)) {
                if (nd->flags & LOOKUP_RCU) {
-                       if (unlikely(unlazy_walk(nd, path->dentry))) {
+                       if (unlikely(nd->path.mnt != path->mnt ||
+                                    unlazy_walk(nd, path->dentry))) {
                                error = -ECHILD;
                                goto out;
                        }
@@ -2888,6 +2925,10 @@ opened:
                        goto exit_fput;
        }
 out:
+       if (unlikely(error > 0)) {
+               WARN_ON(1);
+               error = -EINVAL;
+       }
        if (got_write)
                mnt_drop_write(nd->path.mnt);
        path_put(&save_parent);
@@ -3654,6 +3695,7 @@ retry:
 out_dput:
        done_path_create(&new_path, new_dentry);
        if (retry_estale(error, how)) {
+               path_put(&old_path);
                how |= LOOKUP_REVAL;
                goto retry;
        }