return page;
}
+/*
+ * The logic we want is
+ *
+ * if suid or (sgid and xgrp)
+ * remove privs
+ */
void remove_suid(struct dentry *dentry)
{
- struct iattr newattrs;
- struct inode *inode = dentry->d_inode;
- unsigned int mode = inode->i_mode & (S_ISUID|S_ISGID|S_IXGRP);
+ mode_t mode = dentry->d_inode->i_mode;
+ int kill = 0;
+
+ /* suid always must be killed */
+ if (unlikely(mode & S_ISUID))
+ kill = ATTR_KILL_SUID;
- if (!(mode & S_IXGRP))
- mode &= S_ISUID;
+ /*
+ * sgid without any exec bits is just a mandatory locking mark; leave
+ * it alone. If some exec bits are set, it's a real sgid; kill it.
+ */
+ if (unlikely((mode & S_ISGID) && (mode & S_IXGRP)))
+ kill |= ATTR_KILL_SGID;
- /* were any of the uid bits set? */
- if (mode && !capable(CAP_FSETID)) {
- newattrs.ia_valid = ATTR_KILL_SUID|ATTR_KILL_SGID|ATTR_FORCE;
+ if (unlikely(kill && !capable(CAP_FSETID))) {
+ struct iattr newattrs;
+
+ newattrs.ia_valid = ATTR_FORCE | kill;
notify_change(dentry, &newattrs);
}
}
-
EXPORT_SYMBOL(remove_suid);
/*