[v3,1/2] LSM: mark all set*uid call sites in kernel/sys.c
diff mbox series

Message ID 20190115180421.102209-1-mortonm@chromium.org
State New
Headers show
Series
  • [v3,1/2] LSM: mark all set*uid call sites in kernel/sys.c
Related show

Commit Message

Micah Morton Jan. 15, 2019, 6:04 p.m. UTC
From: Micah Morton <mortonm@chromium.org>

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>
---
These changes used to be part of the main SafeSetID LSM patch set.

 include/linux/capability.h |  5 +++++
 kernel/capability.c        | 19 +++++++++++++++++++
 kernel/sys.c               | 10 +++++-----
 3 files changed, 29 insertions(+), 5 deletions(-)

Comments

Kees Cook Jan. 15, 2019, 7:34 p.m. UTC | #1
On Tue, Jan 15, 2019 at 10:04 AM <mortonm@chromium.org> wrote:
>
> From: Micah Morton <mortonm@chromium.org>
>
> 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>

Reviewed-by: Kees Cook <keescook@chromium.org>

-Kees

> ---
> These changes used to be part of the main SafeSetID LSM patch set.
>
>  include/linux/capability.h |  5 +++++
>  kernel/capability.c        | 19 +++++++++++++++++++
>  kernel/sys.c               | 10 +++++-----
>  3 files changed, 29 insertions(+), 5 deletions(-)
>
> diff --git a/include/linux/capability.h b/include/linux/capability.h
> index f640dcbc880c..c3f9a4d558a0 100644
> --- a/include/linux/capability.h
> +++ b/include/linux/capability.h
> @@ -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);
> diff --git a/kernel/capability.c b/kernel/capability.c
> index 7718d7dcadc7..e0734ace5bc2 100644
> --- a/kernel/capability.c
> +++ b/kernel/capability.c
> @@ -417,6 +417,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
> diff --git a/kernel/sys.c b/kernel/sys.c
> index a48cbf1414b8..a98061c1a124 100644
> --- a/kernel/sys.c
> +++ b/kernel/sys.c
> @@ -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)
> --
> 2.20.1.97.g81188d93c3-goog
>

Patch
diff mbox series

diff --git a/include/linux/capability.h b/include/linux/capability.h
index f640dcbc880c..c3f9a4d558a0 100644
--- a/include/linux/capability.h
+++ b/include/linux/capability.h
@@ -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);
diff --git a/kernel/capability.c b/kernel/capability.c
index 7718d7dcadc7..e0734ace5bc2 100644
--- a/kernel/capability.c
+++ b/kernel/capability.c
@@ -417,6 +417,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
diff --git a/kernel/sys.c b/kernel/sys.c
index a48cbf1414b8..a98061c1a124 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -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)