Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8b01fc86 authored by Jann Horn's avatar Jann Horn Committed by Linus Torvalds
Browse files

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: default avatarJann Horn <jann@thejh.net>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 5224b961
Loading
Loading
Loading
Loading
+48 −28
Original line number Diff line number Diff line
@@ -1275,45 +1275,65 @@ static void check_unsafe_exec(struct linux_binprm *bprm)
	spin_unlock(&p->fs->lock);
}

/*
 * 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)
static void bprm_fill_uid(struct linux_binprm *bprm)
{
	struct inode *inode = file_inode(bprm->file);
	umode_t mode = inode->i_mode;
	int retval;

	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) &&
	    !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 (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 = 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)) {
		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 */
	retval = security_bprm_set_creds(bprm);
	if (retval)