LSM: add SafeSetID module that gates setid calls
authorMicah Morton <mortonm@chromium.org>
Tue, 22 Jan 2019 22:42:09 +0000 (14:42 -0800)
committerJames Morris <james.morris@microsoft.com>
Fri, 25 Jan 2019 19:22:43 +0000 (11:22 -0800)
This change ensures that the set*uid family of syscalls in kernel/sys.c
(setreuid, setuid, setresuid, setfsuid) all call ns_capable_common with
the CAP_OPT_INSETID flag, so capability checks in the security_capable
hook can know whether they are being called from within a set*uid
syscall. This change is a no-op by itself, but is needed for the
proposed SafeSetID LSM.

Signed-off-by: Micah Morton <mortonm@chromium.org>
Acked-by: Kees Cook <keescook@chromium.org>
Signed-off-by: James Morris <james.morris@microsoft.com>
include/linux/capability.h
kernel/capability.c
kernel/sys.c

index f640dcb..c3f9a4d 100644 (file)
@@ -209,6 +209,7 @@ extern bool has_ns_capability_noaudit(struct task_struct *t,
 extern bool capable(int cap);
 extern bool ns_capable(struct user_namespace *ns, int cap);
 extern bool ns_capable_noaudit(struct user_namespace *ns, int cap);
+extern bool ns_capable_setid(struct user_namespace *ns, int cap);
 #else
 static inline bool has_capability(struct task_struct *t, int cap)
 {
@@ -240,6 +241,10 @@ static inline bool ns_capable_noaudit(struct user_namespace *ns, int cap)
 {
        return true;
 }
+static inline bool ns_capable_setid(struct user_namespace *ns, int cap)
+{
+       return true;
+}
 #endif /* CONFIG_MULTIUSER */
 extern bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *inode);
 extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap);
index cfbbcb6..1444f39 100644 (file)
@@ -416,6 +416,25 @@ bool ns_capable_noaudit(struct user_namespace *ns, int cap)
 EXPORT_SYMBOL(ns_capable_noaudit);
 
 /**
+ * ns_capable_setid - Determine if the current task has a superior capability
+ * in effect, while signalling that this check is being done from within a
+ * setid syscall.
+ * @ns:  The usernamespace we want the capability in
+ * @cap: The capability to be tested for
+ *
+ * Return true if the current task has the given superior capability currently
+ * available for use, false if not.
+ *
+ * This sets PF_SUPERPRIV on the task if the capability is available on the
+ * assumption that it's about to be used.
+ */
+bool ns_capable_setid(struct user_namespace *ns, int cap)
+{
+       return ns_capable_common(ns, cap, CAP_OPT_INSETID);
+}
+EXPORT_SYMBOL(ns_capable_setid);
+
+/**
  * capable - Determine if the current task has a superior capability in effect
  * @cap: The capability to be tested for
  *
index f7eb62e..c5f8750 100644 (file)
@@ -516,7 +516,7 @@ long __sys_setreuid(uid_t ruid, uid_t euid)
                new->uid = kruid;
                if (!uid_eq(old->uid, kruid) &&
                    !uid_eq(old->euid, kruid) &&
-                   !ns_capable(old->user_ns, CAP_SETUID))
+                   !ns_capable_setid(old->user_ns, CAP_SETUID))
                        goto error;
        }
 
@@ -525,7 +525,7 @@ long __sys_setreuid(uid_t ruid, uid_t euid)
                if (!uid_eq(old->uid, keuid) &&
                    !uid_eq(old->euid, keuid) &&
                    !uid_eq(old->suid, keuid) &&
-                   !ns_capable(old->user_ns, CAP_SETUID))
+                   !ns_capable_setid(old->user_ns, CAP_SETUID))
                        goto error;
        }
 
@@ -584,7 +584,7 @@ long __sys_setuid(uid_t uid)
        old = current_cred();
 
        retval = -EPERM;
-       if (ns_capable(old->user_ns, CAP_SETUID)) {
+       if (ns_capable_setid(old->user_ns, CAP_SETUID)) {
                new->suid = new->uid = kuid;
                if (!uid_eq(kuid, old->uid)) {
                        retval = set_user(new);
@@ -646,7 +646,7 @@ long __sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
        old = current_cred();
 
        retval = -EPERM;
-       if (!ns_capable(old->user_ns, CAP_SETUID)) {
+       if (!ns_capable_setid(old->user_ns, CAP_SETUID)) {
                if (ruid != (uid_t) -1        && !uid_eq(kruid, old->uid) &&
                    !uid_eq(kruid, old->euid) && !uid_eq(kruid, old->suid))
                        goto error;
@@ -814,7 +814,7 @@ long __sys_setfsuid(uid_t uid)
 
        if (uid_eq(kuid, old->uid)  || uid_eq(kuid, old->euid)  ||
            uid_eq(kuid, old->suid) || uid_eq(kuid, old->fsuid) ||
-           ns_capable(old->user_ns, CAP_SETUID)) {
+           ns_capable_setid(old->user_ns, CAP_SETUID)) {
                if (!uid_eq(kuid, old->fsuid)) {
                        new->fsuid = kuid;
                        if (security_task_fix_setuid(new, old, LSM_SETID_FS) == 0)