Commit c75bbce4 authored by Stephen D. Smalley's avatar Stephen D. Smalley Committed by Linus Torvalds

[PATCH] SELinux: add dynamic context transition support to SELinux

This patch for adds dynamic context transition support to SELinux via writes
to the existing /proc/pid/attr/current interface.

Previously, SELinux only supported exec-based context transitions.  This
functionality allows privileged applications to apply privilege bracketing
without necessarily being refactored to an exec-based model (although such a
model has advantages in least privilege and isolation).

A process must have setcurrent permission to use this mechanism at all, and
the dyntransition permission must be granted between the old and new security
contexts.  Multi-threaded processes are not allowed to use this operation, as
it will yield an inconsistency among the security contexts of the threads
sharing the same mm.

Ptrace permission is revalidated against the new context if the process is
being ptraced.

Author:  Darrel Goeddel <dgoeddel@trustedcs.com>
Signed-off-by: default avatarStephen Smalley <sds@epoch.ncsc.mil>
Signed-off-by: default avatarJames Morris <jmorris@redhat.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 0b6e573e
......@@ -4079,10 +4079,9 @@ static int selinux_setprocattr(struct task_struct *p,
u32 sid = 0;
int error;
if (current != p || !strcmp(name, "current")) {
if (current != p) {
/* SELinux only allows a process to change its own
security attributes, and it only allows the process
current SID to change via exec. */
security attributes. */
return -EACCES;
}
......@@ -4095,6 +4094,8 @@ static int selinux_setprocattr(struct task_struct *p,
error = task_has_perm(current, p, PROCESS__SETEXEC);
else if (!strcmp(name, "fscreate"))
error = task_has_perm(current, p, PROCESS__SETFSCREATE);
else if (!strcmp(name, "current"))
error = task_has_perm(current, p, PROCESS__SETCURRENT);
else
error = -EINVAL;
if (error)
......@@ -4119,6 +4120,51 @@ static int selinux_setprocattr(struct task_struct *p,
tsec->exec_sid = sid;
else if (!strcmp(name, "fscreate"))
tsec->create_sid = sid;
else if (!strcmp(name, "current")) {
struct av_decision avd;
if (sid == 0)
return -EINVAL;
/* Only allow single threaded processes to change context */
if (atomic_read(&p->mm->mm_users) != 1) {
struct task_struct *g, *t;
struct mm_struct *mm = p->mm;
read_lock(&tasklist_lock);
do_each_thread(g, t)
if (t->mm == mm && t != p) {
read_unlock(&tasklist_lock);
return -EPERM;
}
while_each_thread(g, t);
read_unlock(&tasklist_lock);
}
/* Check permissions for the transition. */
error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
PROCESS__DYNTRANSITION, NULL);
if (error)
return error;
/* Check for ptracing, and update the task SID if ok.
Otherwise, leave SID unchanged and fail. */
task_lock(p);
if (p->ptrace & PT_PTRACED) {
error = avc_has_perm_noaudit(tsec->ptrace_sid, sid,
SECCLASS_PROCESS,
PROCESS__PTRACE, &avd);
if (!error)
tsec->sid = sid;
task_unlock(p);
avc_audit(tsec->ptrace_sid, sid, SECCLASS_PROCESS,
PROCESS__PTRACE, &avd, error, NULL);
if (error)
return error;
} else {
tsec->sid = sid;
task_unlock(p);
}
}
else
return -EINVAL;
......
......@@ -62,6 +62,8 @@
S_(SECCLASS_PROCESS, PROCESS__SIGINH, "siginh")
S_(SECCLASS_PROCESS, PROCESS__SETRLIMIT, "setrlimit")
S_(SECCLASS_PROCESS, PROCESS__RLIMITINH, "rlimitinh")
S_(SECCLASS_PROCESS, PROCESS__DYNTRANSITION, "dyntransition")
S_(SECCLASS_PROCESS, PROCESS__SETCURRENT, "setcurrent")
S_(SECCLASS_MSGQ, MSGQ__ENQUEUE, "enqueue")
S_(SECCLASS_MSG, MSG__SEND, "send")
S_(SECCLASS_MSG, MSG__RECEIVE, "receive")
......
......@@ -456,6 +456,8 @@
#define PROCESS__SIGINH 0x00100000UL
#define PROCESS__SETRLIMIT 0x00200000UL
#define PROCESS__RLIMITINH 0x00400000UL
#define PROCESS__DYNTRANSITION 0x00800000UL
#define PROCESS__SETCURRENT 0x01000000UL
#define IPC__CREATE 0x00000001UL
#define IPC__DESTROY 0x00000002UL
......
......@@ -275,7 +275,7 @@ static int context_struct_compute_av(struct context *scontext,
* pair.
*/
if (tclass == SECCLASS_PROCESS &&
(avd->allowed & PROCESS__TRANSITION) &&
(avd->allowed & (PROCESS__TRANSITION | PROCESS__DYNTRANSITION)) &&
scontext->role != tcontext->role) {
for (ra = policydb.role_allow; ra; ra = ra->next) {
if (scontext->role == ra->role &&
......@@ -283,7 +283,8 @@ static int context_struct_compute_av(struct context *scontext,
break;
}
if (!ra)
avd->allowed = (avd->allowed) & ~(PROCESS__TRANSITION);
avd->allowed = (avd->allowed) & ~(PROCESS__TRANSITION |
PROCESS__DYNTRANSITION);
}
return 0;
......
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