diff options
Diffstat (limited to 'fs/exec.c')
| -rw-r--r-- | fs/exec.c | 183 | 
1 files changed, 109 insertions, 74 deletions
| diff --git a/fs/exec.c b/fs/exec.c index ec5df9a38313..1f59ea079cbb 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -55,6 +55,7 @@  #include <asm/uaccess.h>  #include <asm/mmu_context.h>  #include <asm/tlb.h> +#include "internal.h"  #ifdef __alpha__  /* for /sbin/loader handling in search_binary_handler() */ @@ -980,7 +981,7 @@ int flush_old_exec(struct linux_binprm * bprm)  	/* This is the point of no return */  	current->sas_ss_sp = current->sas_ss_size = 0; -	if (current->euid == current->uid && current->egid == current->gid) +	if (current_euid() == current_uid() && current_egid() == current_gid())  		set_dumpable(current->mm, 1);  	else  		set_dumpable(current->mm, suid_dumpable); @@ -1007,16 +1008,17 @@ int flush_old_exec(struct linux_binprm * bprm)  	 */  	current->mm->task_size = TASK_SIZE; -	if (bprm->e_uid != current->euid || bprm->e_gid != current->egid) { -		suid_keys(current); -		set_dumpable(current->mm, suid_dumpable); +	/* install the new credentials */ +	if (bprm->cred->uid != current_euid() || +	    bprm->cred->gid != current_egid()) {  		current->pdeath_signal = 0;  	} else if (file_permission(bprm->file, MAY_READ) || -			(bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP)) { -		suid_keys(current); +		   bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP) {  		set_dumpable(current->mm, suid_dumpable);  	} +	current->personality &= ~bprm->per_clear; +  	/* An exec changes our domain. We are no longer part of the thread  	   group */ @@ -1033,13 +1035,50 @@ out:  EXPORT_SYMBOL(flush_old_exec); +/* + * install the new credentials for this executable + */ +void install_exec_creds(struct linux_binprm *bprm) +{ +	security_bprm_committing_creds(bprm); + +	commit_creds(bprm->cred); +	bprm->cred = NULL; + +	/* cred_exec_mutex must be held at least to this point to prevent +	 * ptrace_attach() from altering our determination of the task's +	 * credentials; any time after this it may be unlocked */ + +	security_bprm_committed_creds(bprm); +} +EXPORT_SYMBOL(install_exec_creds); + +/* + * determine how safe it is to execute the proposed program + * - the caller must hold current->cred_exec_mutex to protect against + *   PTRACE_ATTACH + */ +void check_unsafe_exec(struct linux_binprm *bprm) +{ +	struct task_struct *p = current; + +	bprm->unsafe = tracehook_unsafe_exec(p); + +	if (atomic_read(&p->fs->count) > 1 || +	    atomic_read(&p->files->count) > 1 || +	    atomic_read(&p->sighand->count) > 1) +		bprm->unsafe |= LSM_UNSAFE_SHARE; +} +  /*    * 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 mode; +	umode_t mode;  	struct inode * inode = bprm->file->f_path.dentry->d_inode;  	int retval; @@ -1047,14 +1086,15 @@ int prepare_binprm(struct linux_binprm *bprm)  	if (bprm->file->f_op == NULL)  		return -EACCES; -	bprm->e_uid = current->euid; -	bprm->e_gid = current->egid; +	/* 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)) { +	if (!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)) {  		/* Set-uid? */  		if (mode & S_ISUID) { -			current->personality &= ~PER_CLEAR_ON_SETID; -			bprm->e_uid = inode->i_uid; +			bprm->per_clear |= PER_CLEAR_ON_SETID; +			bprm->cred->euid = inode->i_uid;  		}  		/* Set-gid? */ @@ -1064,52 +1104,23 @@ int prepare_binprm(struct linux_binprm *bprm)  		 * executable.  		 */  		if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { -			current->personality &= ~PER_CLEAR_ON_SETID; -			bprm->e_gid = inode->i_gid; +			bprm->per_clear |= PER_CLEAR_ON_SETID; +			bprm->cred->egid = inode->i_gid;  		}  	}  	/* fill in binprm security blob */ -	retval = security_bprm_set(bprm); +	retval = security_bprm_set_creds(bprm);  	if (retval)  		return retval; +	bprm->cred_prepared = 1; -	memset(bprm->buf,0,BINPRM_BUF_SIZE); -	return kernel_read(bprm->file,0,bprm->buf,BINPRM_BUF_SIZE); +	memset(bprm->buf, 0, BINPRM_BUF_SIZE); +	return kernel_read(bprm->file, 0, bprm->buf, BINPRM_BUF_SIZE);  }  EXPORT_SYMBOL(prepare_binprm); -static int unsafe_exec(struct task_struct *p) -{ -	int unsafe = tracehook_unsafe_exec(p); - -	if (atomic_read(&p->fs->count) > 1 || -	    atomic_read(&p->files->count) > 1 || -	    atomic_read(&p->sighand->count) > 1) -		unsafe |= LSM_UNSAFE_SHARE; - -	return unsafe; -} - -void compute_creds(struct linux_binprm *bprm) -{ -	int unsafe; - -	if (bprm->e_uid != current->uid) { -		suid_keys(current); -		current->pdeath_signal = 0; -	} -	exec_keys(current); - -	task_lock(current); -	unsafe = unsafe_exec(current); -	security_bprm_apply_creds(bprm, unsafe); -	task_unlock(current); -	security_bprm_post_apply_creds(bprm); -} -EXPORT_SYMBOL(compute_creds); -  /*   * Arguments are '\0' separated strings found at the location bprm->p   * points to; chop off the first by relocating brpm->p to right after @@ -1270,6 +1281,8 @@ EXPORT_SYMBOL(search_binary_handler);  void free_bprm(struct linux_binprm *bprm)  {  	free_arg_pages(bprm); +	if (bprm->cred) +		abort_creds(bprm->cred);  	kfree(bprm);  } @@ -1295,10 +1308,20 @@ int do_execve(char * filename,  	if (!bprm)  		goto out_files; +	retval = mutex_lock_interruptible(¤t->cred_exec_mutex); +	if (retval < 0) +		goto out_free; + +	retval = -ENOMEM; +	bprm->cred = prepare_exec_creds(); +	if (!bprm->cred) +		goto out_unlock; +	check_unsafe_exec(bprm); +  	file = open_exec(filename);  	retval = PTR_ERR(file);  	if (IS_ERR(file)) -		goto out_kfree; +		goto out_unlock;  	sched_exec(); @@ -1312,14 +1335,10 @@ int do_execve(char * filename,  	bprm->argc = count(argv, MAX_ARG_STRINGS);  	if ((retval = bprm->argc) < 0) -		goto out_mm; +		goto out;  	bprm->envc = count(envp, MAX_ARG_STRINGS);  	if ((retval = bprm->envc) < 0) -		goto out_mm; - -	retval = security_bprm_alloc(bprm); -	if (retval)  		goto out;  	retval = prepare_binprm(bprm); @@ -1341,21 +1360,18 @@ int do_execve(char * filename,  	current->flags &= ~PF_KTHREAD;  	retval = search_binary_handler(bprm,regs); -	if (retval >= 0) { -		/* execve success */ -		security_bprm_free(bprm); -		acct_update_integrals(current); -		free_bprm(bprm); -		if (displaced) -			put_files_struct(displaced); -		return retval; -	} +	if (retval < 0) +		goto out; -out: -	if (bprm->security) -		security_bprm_free(bprm); +	/* execve succeeded */ +	mutex_unlock(¤t->cred_exec_mutex); +	acct_update_integrals(current); +	free_bprm(bprm); +	if (displaced) +		put_files_struct(displaced); +	return retval; -out_mm: +out:  	if (bprm->mm)  		mmput (bprm->mm); @@ -1364,7 +1380,11 @@ out_file:  		allow_write_access(bprm->file);  		fput(bprm->file);  	} -out_kfree: + +out_unlock: +	mutex_unlock(¤t->cred_exec_mutex); + +out_free:  	free_bprm(bprm);  out_files: @@ -1396,6 +1416,7 @@ EXPORT_SYMBOL(set_binfmt);   */  static int format_corename(char *corename, long signr)  { +	const struct cred *cred = current_cred();  	const char *pat_ptr = core_pattern;  	int ispipe = (*pat_ptr == '|');  	char *out_ptr = corename; @@ -1432,7 +1453,7 @@ static int format_corename(char *corename, long signr)  			/* uid */  			case 'u':  				rc = snprintf(out_ptr, out_end - out_ptr, -					      "%d", current->uid); +					      "%d", cred->uid);  				if (rc > out_end - out_ptr)  					goto out;  				out_ptr += rc; @@ -1440,7 +1461,7 @@ static int format_corename(char *corename, long signr)  			/* gid */  			case 'g':  				rc = snprintf(out_ptr, out_end - out_ptr, -					      "%d", current->gid); +					      "%d", cred->gid);  				if (rc > out_end - out_ptr)  					goto out;  				out_ptr += rc; @@ -1716,8 +1737,9 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)  	struct linux_binfmt * binfmt;  	struct inode * inode;  	struct file * file; +	const struct cred *old_cred; +	struct cred *cred;  	int retval = 0; -	int fsuid = current->fsuid;  	int flag = 0;  	int ispipe = 0;  	unsigned long core_limit = current->signal->rlim[RLIMIT_CORE].rlim_cur; @@ -1730,12 +1752,20 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)  	binfmt = current->binfmt;  	if (!binfmt || !binfmt->core_dump)  		goto fail; + +	cred = prepare_creds(); +	if (!cred) { +		retval = -ENOMEM; +		goto fail; +	} +  	down_write(&mm->mmap_sem);  	/*  	 * If another thread got here first, or we are not dumpable, bail out.  	 */  	if (mm->core_state || !get_dumpable(mm)) {  		up_write(&mm->mmap_sem); +		put_cred(cred);  		goto fail;  	} @@ -1746,12 +1776,16 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)  	 */  	if (get_dumpable(mm) == 2) {	/* Setuid core dump mode */  		flag = O_EXCL;		/* Stop rewrite attacks */ -		current->fsuid = 0;	/* Dump root private */ +		cred->fsuid = 0;	/* Dump root private */  	}  	retval = coredump_wait(exit_code, &core_state); -	if (retval < 0) +	if (retval < 0) { +		put_cred(cred);  		goto fail; +	} + +	old_cred = override_creds(cred);  	/*  	 * Clear any false indication of pending signals that might @@ -1823,7 +1857,7 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)  	 * Dont allow local users get cute and trick others to coredump  	 * into their pre-created files:  	 */ -	if (inode->i_uid != current->fsuid) +	if (inode->i_uid != current_fsuid())  		goto close_fail;  	if (!file->f_op)  		goto close_fail; @@ -1842,7 +1876,8 @@ fail_unlock:  	if (helper_argv)  		argv_free(helper_argv); -	current->fsuid = fsuid; +	revert_creds(old_cred); +	put_cred(cred);  	coredump_finish(mm);  fail:  	return retval; | 
