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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox