[Precise][CVE-2015-3339] fs: take i_mutex during prepare_binprm for set[ug]id executables
John Johansen
john.johansen at canonical.com
Mon Apr 27 18:42:44 UTC 2015
On 04/27/2015 04:12 AM, Luis Henriques wrote:
> From: Jann Horn <jann at thejh.net>
>
> This prevents a race between chown() and execve(), where chowning a
> setuid-user binary to root would momentarily make the binary setuid
> root.
>
> This patch was mostly written by Linus Torvalds.
>
> Signed-off-by: Jann Horn <jann at thejh.net>
> Signed-off-by: Linus Torvalds <torvalds at linux-foundation.org>
> (backported from commit 8b01fc86b9f425899f8a3a8fc1c47d73c2c20543)
> [ luis: backport to Precise:
> - replaced kuid_t/kgid_t by uid_t/gid_t
> - replaced READ_ONCE() by ACCESS_ONCE()
> - replaced task_no_new_privs() by current->no_new_privs
> - replaced file_inode() by bprm->file->f_path.dentry->d_inode
> - dropped user_ns bits ]
> CVE-2015-3339
> BugLink: https://bugs.launchpad.net/bugs/1447373
> Signed-off-by: Luis Henriques <luis.henriques at canonical.com>
Acked-by: John Johansen <john.johansen at canonical.com>
> ---
> fs/exec.c | 69 +++++++++++++++++++++++++++++++++++++++------------------------
> 1 file changed, 43 insertions(+), 26 deletions(-)
>
> diff --git a/fs/exec.c b/fs/exec.c
> index 25a52322ef59..bd70d8612e5a 100644
> --- a/fs/exec.c
> +++ b/fs/exec.c
> @@ -1293,6 +1293,48 @@ int check_unsafe_exec(struct linux_binprm *bprm)
> return res;
> }
>
> +static void bprm_fill_uid(struct linux_binprm *bprm)
> +{
> + struct inode *inode;
> + unsigned int mode;
> + uid_t uid;
> + gid_t gid;
> +
> + /* clear any previous set[ug]id data from a previous binary */
> + bprm->cred->euid = current_euid();
> + bprm->cred->egid = current_egid();
> +
> + if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
> + return;
> +
> + if (current->no_new_privs)
> + return;
> +
> + inode = bprm->file->f_path.dentry->d_inode;
> + mode = ACCESS_ONCE(inode->i_mode);
> + if (!(mode & (S_ISUID|S_ISGID)))
> + return;
> +
> + /* Be careful if suid/sgid is set */
> + mutex_lock(&inode->i_mutex);
> +
> + /* reload atomically mode/uid/gid now that lock held */
> + mode = inode->i_mode;
> + uid = inode->i_uid;
> + gid = inode->i_gid;
> + mutex_unlock(&inode->i_mutex);
> +
> + if (mode & S_ISUID) {
> + bprm->per_clear |= PER_CLEAR_ON_SETID;
> + bprm->cred->euid = uid;
> + }
> +
> + if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
> + bprm->per_clear |= PER_CLEAR_ON_SETID;
> + bprm->cred->egid = gid;
> + }
> +}
> +
> /*
> * Fill the binprm structure from the inode.
> * Check permissions, then read the first 128 (BINPRM_BUF_SIZE) bytes
> @@ -1301,37 +1343,12 @@ int check_unsafe_exec(struct linux_binprm *bprm)
> */
> int prepare_binprm(struct linux_binprm *bprm)
> {
> - umode_t mode;
> - struct inode * inode = bprm->file->f_path.dentry->d_inode;
> int retval;
>
> - mode = inode->i_mode;
> if (bprm->file->f_op == NULL)
> return -EACCES;
>
> - /* clear any previous set[ug]id data from a previous binary */
> - bprm->cred->euid = current_euid();
> - bprm->cred->egid = current_egid();
> -
> - if (!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) &&
> - !current->no_new_privs) {
> - /* Set-uid? */
> - if (mode & S_ISUID) {
> - bprm->per_clear |= PER_CLEAR_ON_SETID;
> - bprm->cred->euid = inode->i_uid;
> - }
> -
> - /* 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->per_clear |= PER_CLEAR_ON_SETID;
> - bprm->cred->egid = inode->i_gid;
> - }
> - }
> + bprm_fill_uid(bprm);
>
> /* fill in binprm security blob */
> retval = security_bprm_set_creds(bprm);
>
More information about the kernel-team
mailing list