aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilly Tarreau <w@1wt.eu>2007-08-15 09:15:09 +0200
committerWilly Tarreau <w@1wt.eu>2007-08-15 09:15:09 +0200
commitc15267679b66e9db3ede1a78213f2efb2e0d6cae (patch)
treebcdb9ecadf9a6532cc7a328bdba1782fb90fb28b
parenta35c6d8698611328d27ed003efc5fa4a6df4fc86 (diff)
[PATCH] CVE-2007-3848 Privilege escalation via PR_SET_PDEATHSIG
Fix the "parent process death signal" vulnerability in the Linux kernel discovered by Wojciech Purczynski of COSEINC PTE Ltd. and iSEC Security Research (CVE-2007-3848). To sum up, any local user could manage to start a setuid program then send it an arbitrary signal while it is running, by first setting the PR_SET_PDEATHSIG argument of the prctl() system call, and then running another setuid program from the parent process. This is something the user is normally supposed to be able to do only as long as the setuid program has not completely switched its UID. Depending on the installed setuid programs, this may lead to either a denial of service or even to a privilege escalation, so this issue is very distribution specific. For more information regarding this issue, please refer to the original advisory : http://www.isec.pl/vulnerabilities/isec-0024-death-signal.txt The following fix has been provided by Solar Designer and is already part of the latest Openwall kernel. Signed-off-by: Willy Tarreau <w@1wt.eu>
-rw-r--r--fs/exec.c14
-rw-r--r--fs/proc/base.c9
2 files changed, 14 insertions, 9 deletions
diff --git a/fs/exec.c b/fs/exec.c
index f196e7e..1d23db6 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -587,6 +587,7 @@ int flush_old_exec(struct linux_binprm * bprm)
{
char * name;
int i, ch, retval;
+ unsigned new_mm_dumpable;
struct signal_struct * oldsig;
struct files_struct * files;
char tcomm[sizeof(current->comm)];
@@ -622,10 +623,12 @@ int flush_old_exec(struct linux_binprm * bprm)
current->sas_ss_sp = current->sas_ss_size = 0;
+ new_mm_dumpable = 0; /* no change */
if (current->euid == current->uid && current->egid == current->gid) {
- current->mm->dumpable = 1;
+ new_mm_dumpable = 1;
current->task_dumpable = 1;
}
+
name = bprm->filename;
for (i=0; (ch = *(name++)) != '\0';) {
if (ch == '/')
@@ -641,9 +644,13 @@ int flush_old_exec(struct linux_binprm * bprm)
de_thread(current);
- if (bprm->e_uid != current->euid || bprm->e_gid != current->egid ||
- permission(bprm->file->f_dentry->d_inode,MAY_READ))
+ if (bprm->e_uid != current->euid || bprm->e_gid != current->egid) {
current->mm->dumpable = 0;
+ current->pdeath_signal = 0;
+ } else if (permission(bprm->file->f_dentry->d_inode, MAY_READ)) {
+ current->mm->dumpable = 0;
+ } else if (new_mm_dumpable)
+ current->mm->dumpable = 1;
/* An exec changes our domain. We are no longer part of the thread
group */
@@ -768,6 +775,7 @@ void compute_creds(struct linux_binprm *bprm)
if (bprm->e_uid != current->uid || bprm->e_gid != current->gid ||
!cap_issubset(new_permitted, current->cap_permitted)) {
current->mm->dumpable = 0;
+ current->pdeath_signal = 0;
lock_kernel();
if (must_not_trace_exec(current)
diff --git a/fs/proc/base.c b/fs/proc/base.c
index d8796bf..eae527c 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -738,15 +738,12 @@ static int proc_base_readdir(struct file * filp,
static int task_dumpable(struct task_struct *task)
{
- int dumpable = 0;
- struct mm_struct *mm;
+ int retval;
task_lock(task);
- mm = task->mm;
- if (mm)
- dumpable = mm->dumpable;
+ retval = is_dumpable(task);
task_unlock(task);
- return dumpable;
+ return retval;
}