mirror of
https://github.com/torvalds/linux
synced 2024-11-05 18:23:50 +00:00
fs: take i_mutex during prepare_binprm for set[ug]id executables
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@thejh.net> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
5224b9613b
commit
8b01fc86b9
1 changed files with 48 additions and 28 deletions
76
fs/exec.c
76
fs/exec.c
|
@ -1275,6 +1275,53 @@ static void check_unsafe_exec(struct linux_binprm *bprm)
|
|||
spin_unlock(&p->fs->lock);
|
||||
}
|
||||
|
||||
static void bprm_fill_uid(struct linux_binprm *bprm)
|
||||
{
|
||||
struct inode *inode;
|
||||
unsigned int mode;
|
||||
kuid_t uid;
|
||||
kgid_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 (task_no_new_privs(current))
|
||||
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) {
|
||||
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
|
||||
|
@ -1283,36 +1330,9 @@ static void check_unsafe_exec(struct linux_binprm *bprm)
|
|||
*/
|
||||
int prepare_binprm(struct linux_binprm *bprm)
|
||||
{
|
||||
struct inode *inode = file_inode(bprm->file);
|
||||
umode_t mode = inode->i_mode;
|
||||
int retval;
|
||||
|
||||
|
||||
/* 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) &&
|
||||
!task_no_new_privs(current) &&
|
||||
kuid_has_mapping(bprm->cred->user_ns, inode->i_uid) &&
|
||||
kgid_has_mapping(bprm->cred->user_ns, inode->i_gid)) {
|
||||
/* 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);
|
||||
|
|
Loading…
Reference in a new issue