Commit 96cc4727 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] SELinux ptrace race fix

From: Stephen Smalley <sds@epoch.ncsc.mil>

Looking again at the SELinux ptrace check, I believe that there is an
unrelated race due to the fact that the parent link is only updated after
releasing the task lock in ptrace_attach (and this is necessary as task lock
doesn't nest with write lock of tasklist_lock).

The patch below changes SELinux to save the tracing process' SID upon a
successful selinux_ptrace hook call and then use that SID in the ptrace check
in apply_creds in order to avoid such races.  This allows us to preserve the
fine-grained process-to-process ptrace check upon exec (vs.  the global
CAP_SYS_PTRACE privilege => PT_PTRACE_CAP flag used by the capability module)
while still avoiding races.
parent c5fe7586
...@@ -119,7 +119,7 @@ static int task_alloc_security(struct task_struct *task) ...@@ -119,7 +119,7 @@ static int task_alloc_security(struct task_struct *task)
memset(tsec, 0, sizeof(struct task_security_struct)); memset(tsec, 0, sizeof(struct task_security_struct));
tsec->magic = SELINUX_MAGIC; tsec->magic = SELINUX_MAGIC;
tsec->task = task; tsec->task = task;
tsec->osid = tsec->sid = SECINITSID_UNLABELED; tsec->osid = tsec->sid = tsec->ptrace_sid = SECINITSID_UNLABELED;
task->security = tsec; task->security = tsec;
return 0; return 0;
...@@ -1331,13 +1331,19 @@ static int post_create(struct inode *dir, ...@@ -1331,13 +1331,19 @@ static int post_create(struct inode *dir,
static int selinux_ptrace(struct task_struct *parent, struct task_struct *child) static int selinux_ptrace(struct task_struct *parent, struct task_struct *child)
{ {
struct task_security_struct *psec = parent->security;
struct task_security_struct *csec = child->security;
int rc; int rc;
rc = secondary_ops->ptrace(parent,child); rc = secondary_ops->ptrace(parent,child);
if (rc) if (rc)
return rc; return rc;
return task_has_perm(parent, child, PROCESS__PTRACE); rc = task_has_perm(parent, child, PROCESS__PTRACE);
/* Save the SID of the tracing process for later use in apply_creds. */
if (!rc)
csec->ptrace_sid = psec->sid;
return rc;
} }
static int selinux_capget(struct task_struct *target, kernel_cap_t *effective, static int selinux_capget(struct task_struct *target, kernel_cap_t *effective,
...@@ -1747,7 +1753,7 @@ static inline void flush_unauthorized_files(struct files_struct * files) ...@@ -1747,7 +1753,7 @@ static inline void flush_unauthorized_files(struct files_struct * files)
static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe) static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
{ {
struct task_security_struct *tsec, *psec; struct task_security_struct *tsec;
struct bprm_security_struct *bsec; struct bprm_security_struct *bsec;
u32 sid; u32 sid;
struct av_decision avd; struct av_decision avd;
...@@ -1782,14 +1788,13 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe) ...@@ -1782,14 +1788,13 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
/* Check for ptracing, and update the task SID if ok. /* Check for ptracing, and update the task SID if ok.
Otherwise, leave SID unchanged and kill. */ Otherwise, leave SID unchanged and kill. */
if (unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { if (unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
psec = current->parent->security; rc = avc_has_perm_noaudit(tsec->ptrace_sid, sid,
rc = avc_has_perm_noaudit(psec->sid, sid,
SECCLASS_PROCESS, PROCESS__PTRACE, SECCLASS_PROCESS, PROCESS__PTRACE,
NULL, &avd); NULL, &avd);
if (!rc) if (!rc)
tsec->sid = sid; tsec->sid = sid;
task_unlock(current); task_unlock(current);
avc_audit(psec->sid, sid, SECCLASS_PROCESS, avc_audit(tsec->ptrace_sid, sid, SECCLASS_PROCESS,
PROCESS__PTRACE, &avd, rc, NULL); PROCESS__PTRACE, &avd, rc, NULL);
if (rc) { if (rc) {
force_sig_specific(SIGKILL, current); force_sig_specific(SIGKILL, current);
......
...@@ -34,6 +34,7 @@ struct task_security_struct { ...@@ -34,6 +34,7 @@ struct task_security_struct {
u32 exec_sid; /* exec SID */ u32 exec_sid; /* exec SID */
u32 create_sid; /* fscreate SID */ u32 create_sid; /* fscreate SID */
struct avc_entry_ref avcr; /* reference to process permissions */ struct avc_entry_ref avcr; /* reference to process permissions */
u32 ptrace_sid; /* SID of ptrace parent */
}; };
struct inode_security_struct { struct inode_security_struct {
......
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