Commit 7f1a6ae7 authored by Jann Horn's avatar Jann Horn Committed by Sasha Levin

fs: take i_mutex during prepare_binprm for set[ug]id executables

[ Upstream commit 8b01fc86 ]

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: default avatarJann Horn <jann@thejh.net>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: default avatarSasha Levin <sasha.levin@oracle.com>
parent c42f3e15
...@@ -1250,44 +1250,64 @@ static void check_unsafe_exec(struct linux_binprm *bprm) ...@@ -1250,44 +1250,64 @@ static void check_unsafe_exec(struct linux_binprm *bprm)
spin_unlock(&p->fs->lock); spin_unlock(&p->fs->lock);
} }
/* static void bprm_fill_uid(struct linux_binprm *bprm)
* Fill the binprm structure from the inode.
* Check permissions, then read the first 128 (BINPRM_BUF_SIZE) bytes
*
* This may be called multiple times for binary chains (scripts for example).
*/
int prepare_binprm(struct linux_binprm *bprm)
{ {
struct inode *inode = file_inode(bprm->file); struct inode *inode;
umode_t mode = inode->i_mode; unsigned int mode;
int retval; kuid_t uid;
kgid_t gid;
/* clear any previous set[ug]id data from a previous binary */ /* clear any previous set[ug]id data from a previous binary */
bprm->cred->euid = current_euid(); bprm->cred->euid = current_euid();
bprm->cred->egid = current_egid(); bprm->cred->egid = current_egid();
if (!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) && if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
!task_no_new_privs(current) && return;
kuid_has_mapping(bprm->cred->user_ns, inode->i_uid) &&
kgid_has_mapping(bprm->cred->user_ns, inode->i_gid)) { if (task_no_new_privs(current))
/* Set-uid? */ return;
inode = file_inode(bprm->file);
mode = READ_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);
/* We ignore suid/sgid if there are no mappings for them in the ns */
if (!kuid_has_mapping(bprm->cred->user_ns, uid) ||
!kgid_has_mapping(bprm->cred->user_ns, gid))
return;
if (mode & S_ISUID) { if (mode & S_ISUID) {
bprm->per_clear |= PER_CLEAR_ON_SETID; bprm->per_clear |= PER_CLEAR_ON_SETID;
bprm->cred->euid = inode->i_uid; bprm->cred->euid = 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)) { if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
bprm->per_clear |= PER_CLEAR_ON_SETID; bprm->per_clear |= PER_CLEAR_ON_SETID;
bprm->cred->egid = inode->i_gid; bprm->cred->egid = gid;
}
} }
}
/*
* Fill the binprm structure from the inode.
* Check permissions, then read the first 128 (BINPRM_BUF_SIZE) bytes
*
* This may be called multiple times for binary chains (scripts for example).
*/
int prepare_binprm(struct linux_binprm *bprm)
{
int retval;
bprm_fill_uid(bprm);
/* fill in binprm security blob */ /* fill in binprm security blob */
retval = security_bprm_set_creds(bprm); retval = security_bprm_set_creds(bprm);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment