WIP: update tizen_qemu_defconfig
[platform/kernel/linux-starfive.git] / fs / attr.c
index 1552a5f..b45f30e 100644 (file)
--- a/fs/attr.c
+++ b/fs/attr.c
 #include <linux/evm.h>
 #include <linux/ima.h>
 
+#include "internal.h"
+
+/**
+ * setattr_should_drop_sgid - determine whether the setgid bit needs to be
+ *                            removed
+ * @mnt_userns:        user namespace of the mount @inode was found from
+ * @inode:     inode to check
+ *
+ * This function determines whether the setgid bit needs to be removed.
+ * We retain backwards compatibility and require setgid bit to be removed
+ * unconditionally if S_IXGRP is set. Otherwise we have the exact same
+ * requirements as setattr_prepare() and setattr_copy().
+ *
+ * Return: ATTR_KILL_SGID if setgid bit needs to be removed, 0 otherwise.
+ */
+int setattr_should_drop_sgid(struct user_namespace *mnt_userns,
+                            const struct inode *inode)
+{
+       umode_t mode = inode->i_mode;
+
+       if (!(mode & S_ISGID))
+               return 0;
+       if (mode & S_IXGRP)
+               return ATTR_KILL_SGID;
+       if (!in_group_or_capable(mnt_userns, inode,
+                                i_gid_into_vfsgid(mnt_userns, inode)))
+               return ATTR_KILL_SGID;
+       return 0;
+}
+
+/**
+ * setattr_should_drop_suidgid - determine whether the set{g,u}id bit needs to
+ *                               be dropped
+ * @mnt_userns:        user namespace of the mount @inode was found from
+ * @inode:     inode to check
+ *
+ * This function determines whether the set{g,u}id bits need to be removed.
+ * If the setuid bit needs to be removed ATTR_KILL_SUID is returned. If the
+ * setgid bit needs to be removed ATTR_KILL_SGID is returned. If both
+ * set{g,u}id bits need to be removed the corresponding mask of both flags is
+ * returned.
+ *
+ * Return: A mask of ATTR_KILL_S{G,U}ID indicating which - if any - setid bits
+ * to remove, 0 otherwise.
+ */
+int setattr_should_drop_suidgid(struct user_namespace *mnt_userns,
+                               struct inode *inode)
+{
+       umode_t mode = inode->i_mode;
+       int kill = 0;
+
+       /* suid always must be killed */
+       if (unlikely(mode & S_ISUID))
+               kill = ATTR_KILL_SUID;
+
+       kill |= setattr_should_drop_sgid(mnt_userns, inode);
+
+       if (unlikely(kill && !capable(CAP_FSETID) && S_ISREG(mode)))
+               return kill;
+
+       return 0;
+}
+EXPORT_SYMBOL(setattr_should_drop_suidgid);
+
 /**
  * chown_ok - verify permissions to chown inode
  * @mnt_userns:        user namespace of the mount @inode was found from
@@ -140,8 +204,7 @@ int setattr_prepare(struct user_namespace *mnt_userns, struct dentry *dentry,
                        vfsgid = i_gid_into_vfsgid(mnt_userns, inode);
 
                /* Also check the setgid bit! */
-               if (!vfsgid_in_group_p(vfsgid) &&
-                   !capable_wrt_inode_uidgid(mnt_userns, inode, CAP_FSETID))
+               if (!in_group_or_capable(mnt_userns, inode, vfsgid))
                        attr->ia_mode &= ~S_ISGID;
        }
 
@@ -251,9 +314,8 @@ void setattr_copy(struct user_namespace *mnt_userns, struct inode *inode,
                inode->i_ctime = attr->ia_ctime;
        if (ia_valid & ATTR_MODE) {
                umode_t mode = attr->ia_mode;
-               vfsgid_t vfsgid = i_gid_into_vfsgid(mnt_userns, inode);
-               if (!vfsgid_in_group_p(vfsgid) &&
-                   !capable_wrt_inode_uidgid(mnt_userns, inode, CAP_FSETID))
+               if (!in_group_or_capable(mnt_userns, inode,
+                                        i_gid_into_vfsgid(mnt_userns, inode)))
                        mode &= ~S_ISGID;
                inode->i_mode = mode;
        }
@@ -375,7 +437,7 @@ int notify_change(struct user_namespace *mnt_userns, struct dentry *dentry,
                }
        }
        if (ia_valid & ATTR_KILL_SGID) {
-               if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
+               if (mode & S_ISGID) {
                        if (!(ia_valid & ATTR_MODE)) {
                                ia_valid = attr->ia_valid |= ATTR_MODE;
                                attr->ia_mode = inode->i_mode;