From: sergeh@kernel.org
To: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Kees Cook <keescook@chromium.org>,
Max Kellermann <max.kellermann@ionos.com>,
"Serge E. Hallyn" <serge@hallyn.com>,
paul@paul-moore.com, jmorris@namei.org,
Andy Lutomirski <luto@kernel.org>,
morgan@kernel.org, Christian Brauner <christian@brauner.io>,
linux-security-module@vger.kernel.org,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH] exec: Correct the permission check for unsafe exec
Date: Fri, 16 May 2025 21:49:23 +0000 [thread overview]
Message-ID: <aCey42ACEr4h3fmH@lei> (raw)
In-Reply-To: <878qmxsuy8.fsf@email.froward.int.ebiederm.org>
On Thu, May 15, 2025 at 11:24:47AM -0500, Eric W. Biederman wrote:
>
> Max Kellerman recently experienced a problem[1] when calling exec with
> differing uid and euid's and he triggered the logic that is supposed
> to only handle setuid executables.
>
> When exec isn't changing anything in struct cred it doesn't make sense
> to go into the code that is there to handle the case when the
> credentials change.
>
> When looking into the history of the code I discovered that this issue
> was not present in Linux-2.4.0-test12 and was introduced in
> Linux-2.4.0-prerelease when the logic for handling this case was moved
> from prepare_binprm to compute_creds in fs/exec.c.
>
> The bug introduced was to comparing euid in the new credentials with
> uid instead of euid in the old credentials, when testing if setuid
> had changed the euid.
>
> Since triggering the keep ptrace limping along case for setuid
> executables makes no sense when it was not a setuid exec revert back
> to the logic present in Linux-2.4.0-test12.
>
> This removes the confusingly named and subtlety incorrect helpers
> is_setuid and is_setgid, that helped this bug to persist.
>
> The variable is_setid is renamed to id_changed (it's Linux-2.4.0-test12
> name) as the old name describes matters rather than it's cause.
>
> The code removed in Linux-2.4.0-prerelease was:
> - /* Set-uid? */
> - if (mode & S_ISUID) {
> - bprm->e_uid = inode->i_uid;
> - if (bprm->e_uid != current->euid)
> - id_change = 1;
> - }
> -
> - /* Set-gid? */
> - /*
> - * If setgid is set but no group execute bit then this
> - * is a candidate for mandatory locking, not a setgid
> - * executable.
> - */
> - if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
> - bprm->e_gid = inode->i_gid;
> - if (!in_group_p(bprm->e_gid))
> - id_change = 1;
>
> Linux-2.4.0-prerelease added the current logic as:
> + if (bprm->e_uid != current->uid || bprm->e_gid != current->gid ||
> + !cap_issubset(new_permitted, current->cap_permitted)) {
> + current->dumpable = 0;
> +
> + lock_kernel();
> + if (must_not_trace_exec(current)
> + || atomic_read(¤t->fs->count) > 1
> + || atomic_read(¤t->files->count) > 1
> + || atomic_read(¤t->sig->count) > 1) {
> + if(!capable(CAP_SETUID)) {
> + bprm->e_uid = current->uid;
> + bprm->e_gid = current->gid;
> + }
> + if(!capable(CAP_SETPCAP)) {
> + new_permitted = cap_intersect(new_permitted,
> + current->cap_permitted);
> + }
> + }
> + do_unlock = 1;
> + }
>
> I have condensed the logic from Linux-2.4.0-test12 to just:
> id_changed = !uid_eq(new->euid, old->euid) || !in_group_p(new->egid);
>
>
> This change is userspace visible, but I don't expect anyone to care.
>
> For the bug that is being fixed to trigger bprm->unsafe has to be set.
> The variable bprm->unsafe is set when ptracing an executable, when
> sharing a working directory, or when no_new_privs is set. Properly
> testing for cases that are safe even in those conditions and doing
> nothing special should not affect anyone. Especially if they were
> previously ok with their credentials getting munged
>
> Reported-by: Max Kellermann <max.kellermann@ionos.com>
> Fixes: 64444d3d0d7f ("Linux version 2.4.0-prerelease")
> [1] https://lkml.kernel.org/r/20250306082615.174777-1-max.kellermann@ionos.com
> Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
Thanks, Eric.
Obviously we should continue to discuss Jann's concern and the
potential change in behavior, but I'm hugely in favor of this patch.
Reviewed-by: Serge Hallyn <serge@hallyn.com>
> ---
> security/commoncap.c | 18 ++++++------------
> 1 file changed, 6 insertions(+), 12 deletions(-)
>
> diff --git a/security/commoncap.c b/security/commoncap.c
> index 28d4248bf001..96c7654f2012 100644
> --- a/security/commoncap.c
> +++ b/security/commoncap.c
> @@ -856,12 +856,6 @@ static void handle_privileged_root(struct linux_binprm *bprm, bool has_fcap,
> #define __cap_full(field, cred) \
> cap_issubset(CAP_FULL_SET, cred->cap_##field)
>
> -static inline bool __is_setuid(struct cred *new, const struct cred *old)
> -{ return !uid_eq(new->euid, old->uid); }
> -
> -static inline bool __is_setgid(struct cred *new, const struct cred *old)
> -{ return !gid_eq(new->egid, old->gid); }
> -
> /*
> * 1) Audit candidate if current->cap_effective is set
> *
> @@ -891,7 +885,7 @@ static inline bool nonroot_raised_pE(struct cred *new, const struct cred *old,
> (root_privileged() &&
> __is_suid(root, new) &&
> !__cap_full(effective, new)) ||
> - (!__is_setuid(new, old) &&
> + (uid_eq(new->euid, old->euid) &&
> ((has_fcap &&
> __cap_gained(permitted, new, old)) ||
> __cap_gained(ambient, new, old))))
> @@ -917,7 +911,7 @@ int cap_bprm_creds_from_file(struct linux_binprm *bprm, const struct file *file)
> /* Process setpcap binaries and capabilities for uid 0 */
> const struct cred *old = current_cred();
> struct cred *new = bprm->cred;
> - bool effective = false, has_fcap = false, is_setid;
> + bool effective = false, has_fcap = false, id_changed;
> int ret;
> kuid_t root_uid;
>
> @@ -941,9 +935,9 @@ int cap_bprm_creds_from_file(struct linux_binprm *bprm, const struct file *file)
> *
> * In addition, if NO_NEW_PRIVS, then ensure we get no new privs.
> */
> - is_setid = __is_setuid(new, old) || __is_setgid(new, old);
> + id_changed = !uid_eq(new->euid, old->euid) || !in_group_p(new->egid);
>
> - if ((is_setid || __cap_gained(permitted, new, old)) &&
> + if ((id_changed || __cap_gained(permitted, new, old)) &&
> ((bprm->unsafe & ~LSM_UNSAFE_PTRACE) ||
> !ptracer_capable(current, new->user_ns))) {
> /* downgrade; they get no more than they had, and maybe less */
> @@ -960,7 +954,7 @@ int cap_bprm_creds_from_file(struct linux_binprm *bprm, const struct file *file)
> new->sgid = new->fsgid = new->egid;
>
> /* File caps or setid cancels ambient. */
> - if (has_fcap || is_setid)
> + if (has_fcap || id_changed)
> cap_clear(new->cap_ambient);
>
> /*
> @@ -993,7 +987,7 @@ int cap_bprm_creds_from_file(struct linux_binprm *bprm, const struct file *file)
> return -EPERM;
>
> /* Check for privilege-elevated exec. */
> - if (is_setid ||
> + if (id_changed ||
> (!__is_real(root_uid, new) &&
> (effective ||
> __cap_grew(permitted, ambient, new))))
> --
> 2.41.0
>
prev parent reply other threads:[~2025-05-16 21:49 UTC|newest]
Thread overview: 43+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-03-06 8:26 [PATCH] security/commoncap: don't assume "setid" if all ids are identical Max Kellermann
2025-03-07 10:32 ` kernel test robot
2025-03-09 15:19 ` Serge E. Hallyn
2025-04-28 11:43 ` Max Kellermann
2025-05-06 13:21 ` Serge E. Hallyn
2025-05-06 14:51 ` Max Kellermann
2025-05-07 3:16 ` Andrew G. Morgan
2025-05-07 6:33 ` Max Kellermann
2025-05-08 3:32 ` Andrew G. Morgan
2025-05-08 6:38 ` Max Kellermann
2025-05-08 8:37 ` Max Kellermann
2025-05-09 17:50 ` Max Kellermann
2025-05-08 22:12 ` sergeh
2025-05-09 6:15 ` Max Kellermann
2025-05-09 14:44 ` Eric W. Biederman
2025-05-09 16:53 ` Max Kellermann
2025-05-09 20:17 ` Serge E. Hallyn
2025-05-09 18:41 ` [PATCH] Documentation/no_new_privs.rst: document dropping effective ids Max Kellermann
2025-05-15 16:24 ` [PATCH] exec: Correct the permission check for unsafe exec Eric W. Biederman
2025-05-15 22:09 ` Kees Cook
2025-05-16 15:26 ` Eric W. Biederman
2025-05-16 18:06 ` Jann Horn
2025-05-16 18:08 ` Jann Horn
2025-05-16 21:46 ` sergeh
2025-05-20 22:38 ` Jann Horn
2025-05-20 22:43 ` Kees Cook
2025-05-16 23:29 ` Eric W. Biederman
2025-05-20 20:20 ` Kees Cook
2025-05-20 22:13 ` [PATCH v2] " Eric W. Biederman
2025-05-20 22:35 ` Kees Cook
2025-05-20 23:53 ` Jann Horn
2025-05-21 15:27 ` Eric W. Biederman
2025-05-21 15:36 ` Jann Horn
2025-06-11 0:18 ` Paul Moore
2025-06-11 14:23 ` Max Kellermann
2025-06-13 15:07 ` Eric W. Biederman
2025-06-12 21:26 ` Serge E. Hallyn
2025-06-13 1:48 ` Kees Cook
2025-06-13 15:28 ` Paul Moore
2025-06-16 19:57 ` Kees Cook
2025-06-16 20:16 ` Paul Moore
2025-05-16 21:48 ` [PATCH] " sergeh
2025-05-16 21:49 ` sergeh [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aCey42ACEr4h3fmH@lei \
--to=sergeh@kernel.org \
--cc=christian@brauner.io \
--cc=ebiederm@xmission.com \
--cc=jmorris@namei.org \
--cc=keescook@chromium.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=luto@kernel.org \
--cc=max.kellermann@ionos.com \
--cc=morgan@kernel.org \
--cc=paul@paul-moore.com \
--cc=serge@hallyn.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.