Skip to content

Commit

Permalink
SELinux: allow preemption between transition permission checks
Browse files Browse the repository at this point in the history
In security_get_user_sids, move the transition permission checks
outside of the section holding the policy rdlock, and use the AVC to
perform the checks, calling cond_resched after each one.  These
changes should allow preemption between the individual checks and
enable caching of the results.  It may however increase the overall
time spent in the function in some cases, particularly in the cache
miss case.

The long term fix will be to take much of this logic to userspace by
exporting additional state via selinuxfs, and ultimately deprecating
and eliminating this interface from the kernel.

Tested-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by:  Stephen Smalley <sds@tycho.nsa.gov>
Signed-off-by: James Morris <jmorris@namei.org>
  • Loading branch information
Stephen Smalley authored and James Morris committed Jul 12, 2007
1 parent 9dc9978 commit 2c3c05d
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 29 deletions.
10 changes: 6 additions & 4 deletions security/selinux/avc.c
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,7 @@ int avc_ss_reset(u32 seqno)
* @tsid: target security identifier
* @tclass: target security class
* @requested: requested permissions, interpreted based on @tclass
* @flags: AVC_STRICT or 0
* @avd: access vector decisions
*
* Check the AVC to determine whether the @requested permissions are granted
Expand All @@ -846,8 +847,9 @@ int avc_ss_reset(u32 seqno)
* should be released for the auditing.
*/
int avc_has_perm_noaudit(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
struct av_decision *avd)
u16 tclass, u32 requested,
unsigned flags,
struct av_decision *avd)
{
struct avc_node *node;
struct avc_entry entry, *p_ae;
Expand All @@ -874,7 +876,7 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
denied = requested & ~(p_ae->avd.allowed);

if (!requested || denied) {
if (selinux_enforcing)
if (selinux_enforcing || (flags & AVC_STRICT))
rc = -EACCES;
else
if (node)
Expand Down Expand Up @@ -909,7 +911,7 @@ int avc_has_perm(u32 ssid, u32 tsid, u16 tclass,
struct av_decision avd;
int rc;

rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, &avd);
rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd);
avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
return rc;
}
9 changes: 5 additions & 4 deletions security/selinux/hooks.c
Original file line number Diff line number Diff line change
Expand Up @@ -1592,9 +1592,10 @@ static int selinux_vm_enough_memory(long pages)
rc = secondary_ops->capable(current, CAP_SYS_ADMIN);
if (rc == 0)
rc = avc_has_perm_noaudit(tsec->sid, tsec->sid,
SECCLASS_CAPABILITY,
CAP_TO_MASK(CAP_SYS_ADMIN),
NULL);
SECCLASS_CAPABILITY,
CAP_TO_MASK(CAP_SYS_ADMIN),
0,
NULL);

if (rc == 0)
cap_sys_admin = 1;
Expand Down Expand Up @@ -4626,7 +4627,7 @@ static int selinux_setprocattr(struct task_struct *p,
if (p->ptrace & PT_PTRACED) {
error = avc_has_perm_noaudit(tsec->ptrace_sid, sid,
SECCLASS_PROCESS,
PROCESS__PTRACE, &avd);
PROCESS__PTRACE, 0, &avd);
if (!error)
tsec->sid = sid;
task_unlock(p);
Expand Down
6 changes: 4 additions & 2 deletions security/selinux/include/avc.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,11 @@ void avc_audit(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
struct av_decision *avd, int result, struct avc_audit_data *auditdata);

#define AVC_STRICT 1 /* Ignore permissive mode. */
int avc_has_perm_noaudit(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
struct av_decision *avd);
u16 tclass, u32 requested,
unsigned flags,
struct av_decision *avd);

int avc_has_perm(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
Expand Down
49 changes: 30 additions & 19 deletions security/selinux/ss/services.c
Original file line number Diff line number Diff line change
Expand Up @@ -1587,19 +1587,18 @@ int security_get_user_sids(u32 fromsid,
u32 *nel)
{
struct context *fromcon, usercon;
u32 *mysids, *mysids2, sid;
u32 *mysids = NULL, *mysids2, sid;
u32 mynel = 0, maxnel = SIDS_NEL;
struct user_datum *user;
struct role_datum *role;
struct av_decision avd;
struct ebitmap_node *rnode, *tnode;
int rc = 0, i, j;

if (!ss_initialized) {
*sids = NULL;
*nel = 0;
*sids = NULL;
*nel = 0;

if (!ss_initialized)
goto out;
}

POLICY_RDLOCK;

Expand Down Expand Up @@ -1635,25 +1634,16 @@ int security_get_user_sids(u32 fromsid,
if (mls_setup_user_range(fromcon, user, &usercon))
continue;

rc = context_struct_compute_av(fromcon, &usercon,
SECCLASS_PROCESS,
PROCESS__TRANSITION,
&avd);
if (rc || !(avd.allowed & PROCESS__TRANSITION))
continue;
rc = sidtab_context_to_sid(&sidtab, &usercon, &sid);
if (rc) {
kfree(mysids);
if (rc)
goto out_unlock;
}
if (mynel < maxnel) {
mysids[mynel++] = sid;
} else {
maxnel += SIDS_NEL;
mysids2 = kcalloc(maxnel, sizeof(*mysids2), GFP_ATOMIC);
if (!mysids2) {
rc = -ENOMEM;
kfree(mysids);
goto out_unlock;
}
memcpy(mysids2, mysids, mynel * sizeof(*mysids2));
Expand All @@ -1664,11 +1654,32 @@ int security_get_user_sids(u32 fromsid,
}
}

*sids = mysids;
*nel = mynel;

out_unlock:
POLICY_RDUNLOCK;
if (rc || !mynel) {
kfree(mysids);
goto out;
}

mysids2 = kcalloc(mynel, sizeof(*mysids2), GFP_KERNEL);
if (!mysids2) {
rc = -ENOMEM;
kfree(mysids);
goto out;
}
for (i = 0, j = 0; i < mynel; i++) {
rc = avc_has_perm_noaudit(fromsid, mysids[i],
SECCLASS_PROCESS,
PROCESS__TRANSITION, AVC_STRICT,
NULL);
if (!rc)
mysids2[j++] = mysids[i];
cond_resched();
}
rc = 0;
kfree(mysids);
*sids = mysids2;
*nel = j;
out:
return rc;
}
Expand Down

0 comments on commit 2c3c05d

Please sign in to comment.