This patch enables applications to handle permissive domain correctly.

Since the v2.6.26 kernel, SELinux has supported an idea of permissive
domain which allows certain processes to work as if permissive mode,
even if the global setting is enforcing mode.
However, we don't have an application program interface to inform
what domains are permissive one, and what domains are not.
It means applications focuses on SELinux (XACE/SELinux, SE-PostgreSQL
and so on) cannot handle permissive domain correctly.

This patch add the sixth field (flags) on the reply of the /selinux/access
interface which is used to make an access control decision from userspace.
If the first bit of the flags field is positive, it means the required
access control decision is on permissive domain, so application should
allow any required actions, as the kernel doing.

This patch also has a side benefit. The av_decision.flags is set at
context_struct_compute_av(). It enables to check required permissions
without read_lock(&policy_rwlock).

Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com>
This commit is contained in:
KaiGai Kohei 2009-04-07 22:10:30 -04:00 committed by Eamon Walsh
parent 318748d659
commit 55ed6e7fa6
6 changed files with 122 additions and 26 deletions

View file

@ -130,8 +130,12 @@ struct av_decision {
access_vector_t auditallow; access_vector_t auditallow;
access_vector_t auditdeny; access_vector_t auditdeny;
unsigned int seqno; unsigned int seqno;
unsigned int flags;
}; };
/* Definitions of av_decision.flags */
#define SELINUX_AVD_FLAGS_PERMISSIVE 0x0001
/* Structure for passing options, used by AVC and label subsystems */ /* Structure for passing options, used by AVC and label subsystems */
struct selinux_opt { struct selinux_opt {
int type; int type;
@ -186,6 +190,17 @@ extern int security_compute_av_raw(security_context_t scon,
access_vector_t requested, access_vector_t requested,
struct av_decision *avd); struct av_decision *avd);
extern int security_compute_av_flags(security_context_t scon,
security_context_t tcon,
security_class_t tclass,
access_vector_t requested,
struct av_decision *avd);
extern int security_compute_av_flags_raw(security_context_t scon,
security_context_t tcon,
security_class_t tclass,
access_vector_t requested,
struct av_decision *avd);
/* Compute a labeling decision and set *newcon to refer to it. /* Compute a labeling decision and set *newcon to refer to it.
Caller must free via freecon. */ Caller must free via freecon. */
extern int security_compute_create(security_context_t scon, extern int security_compute_create(security_context_t scon,

View file

@ -1,6 +1,6 @@
.TH "security_compute_av" "3" "1 January 2004" "russell@coker.com.au" "SELinux API documentation" .TH "security_compute_av" "3" "1 January 2004" "russell@coker.com.au" "SELinux API documentation"
.SH "NAME" .SH "NAME"
security_compute_av, security_compute_create, security_compute_relabel, security_compute_av, security_compute_av_flags, security_compute_create, security_compute_relabel,
security_compute_member, security_compute_user, security_get_initial_context \- query security_compute_member, security_compute_user, security_get_initial_context \- query
the SELinux policy database in the kernel. the SELinux policy database in the kernel.
@ -11,6 +11,8 @@ the SELinux policy database in the kernel.
.sp .sp
.BI "int security_compute_av(security_context_t "scon ", security_context_t "tcon ", security_class_t "tclass ", access_vector_t "requested ", struct av_decision *" avd ); .BI "int security_compute_av(security_context_t "scon ", security_context_t "tcon ", security_class_t "tclass ", access_vector_t "requested ", struct av_decision *" avd );
.sp .sp
.BI "int security_compute_av_flags(security_context_t "scon ", security_context_t "tcon ", security_class_t "tclass ", access_vector_t "requested ", struct av_decision *" avd );
.sp
.BI "int security_compute_create(security_context_t "scon ", security_context_t "tcon ", security_class_t "tclass ", security_context_t *" newcon ); .BI "int security_compute_create(security_context_t "scon ", security_context_t "tcon ", security_class_t "tclass ", security_context_t *" newcon );
.sp .sp
.BI "int security_compute_relabel(security_context_t "scon ", security_context_t "tcon ", security_class_t "tclass ", security_context_t *" newcon ); .BI "int security_compute_relabel(security_context_t "scon ", security_context_t "tcon ", security_class_t "tclass ", security_context_t *" newcon );
@ -34,7 +36,19 @@ via class
.B tclass .B tclass
with the with the
.B requested .B requested
access vector. See the cron source for a usage example. access vector. The decision is returned in
.BR avd .
.B security_compute_av_flags
is identical to
.B security_compute_av
but additionally sets the
.B flags
field of
.BR avd .
Currently one flag is supported:
.BR SELINUX_AVD_FLAGS_PERMISSIVE ,
which indicates the decision is computed on a permissive domain.
.B security_compute_create .B security_compute_create
is used to compute a context to use for labeling a new object in a particular is used to compute a context to use for labeling a new object in a particular

View file

@ -0,0 +1 @@
.so man3/security_compute_av.3

View file

@ -849,9 +849,9 @@ int avc_has_perm_noaudit(security_id_t ssid,
rc = -1; rc = -1;
goto out; goto out;
} }
rc = security_compute_av_raw(ssid->ctx, tsid->ctx, rc = security_compute_av_flags_raw(ssid->ctx, tsid->ctx,
tclass, requested, tclass, requested,
&entry.avd); &entry.avd);
if (rc) if (rc)
goto out; goto out;
rc = avc_insert(ssid, tsid, tclass, &entry, aeref); rc = avc_insert(ssid, tsid, tclass, &entry, aeref);
@ -867,11 +867,13 @@ int avc_has_perm_noaudit(security_id_t ssid,
denied = requested & ~(ae->avd.allowed); denied = requested & ~(ae->avd.allowed);
if (!requested || denied) { if (!requested || denied) {
if (avc_enforcing) { if (!avc_enforcing ||
(ae->avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE))
ae->avd.allowed |= requested;
else {
errno = EACCES; errno = EACCES;
rc = -1; rc = -1;
} else }
ae->avd.allowed |= requested;
} }
out: out:
@ -885,9 +887,11 @@ int avc_has_perm(security_id_t ssid, security_id_t tsid,
security_class_t tclass, access_vector_t requested, security_class_t tclass, access_vector_t requested,
struct avc_entry_ref *aeref, void *auditdata) struct avc_entry_ref *aeref, void *auditdata)
{ {
struct av_decision avd = { 0, 0, 0, 0, 0 }; struct av_decision avd;
int errsave, rc; int errsave, rc;
memset(&avd, 0, sizeof(avd));
rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, aeref, &avd); rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, aeref, &avd);
errsave = errno; errsave = errno;
avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata); avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
@ -917,8 +921,8 @@ int avc_compute_create(security_id_t ssid, security_id_t tsid,
rc = avc_lookup(ssid, tsid, tclass, 0, &aeref); rc = avc_lookup(ssid, tsid, tclass, 0, &aeref);
if (rc) { if (rc) {
/* need to make a cache entry for this tuple */ /* need to make a cache entry for this tuple */
rc = security_compute_av_raw(ssid->ctx, tsid->ctx, rc = security_compute_av_flags_raw(ssid->ctx, tsid->ctx,
tclass, 0, &entry.avd); tclass, 0, &entry.avd);
if (rc) if (rc)
goto out; goto out;
rc = avc_insert(ssid, tsid, tclass, &entry, &aeref); rc = avc_insert(ssid, tsid, tclass, &entry, &aeref);

View file

@ -10,10 +10,11 @@
#include "policy.h" #include "policy.h"
#include "mapping.h" #include "mapping.h"
int security_compute_av_raw(security_context_t scon, int security_compute_av_flags_raw(security_context_t scon,
security_context_t tcon, security_context_t tcon,
security_class_t tclass, security_class_t tclass,
access_vector_t requested, struct av_decision *avd) access_vector_t requested,
struct av_decision *avd)
{ {
char path[PATH_MAX]; char path[PATH_MAX];
char *buf; char *buf;
@ -49,12 +50,15 @@ int security_compute_av_raw(security_context_t scon,
if (ret < 0) if (ret < 0)
goto out2; goto out2;
if (sscanf(buf, "%x %x %x %x %u", &avd->allowed, ret = sscanf(buf, "%x %x %x %x %u %x",
&avd->decided, &avd->auditallow, &avd->auditdeny, &avd->allowed, &avd->decided,
&avd->seqno) != 5) { &avd->auditallow, &avd->auditdeny,
&avd->seqno, &avd->flags);
if (ret < 5) {
ret = -1; ret = -1;
goto out2; goto out2;
} } else if (ret < 6)
avd->flags = 0;
map_decision(tclass, avd); map_decision(tclass, avd);
@ -66,16 +70,44 @@ int security_compute_av_raw(security_context_t scon,
return ret; return ret;
} }
hidden_def(security_compute_av_flags_raw)
int security_compute_av_raw(security_context_t scon,
security_context_t tcon,
security_class_t tclass,
access_vector_t requested,
struct av_decision *avd)
{
struct av_decision lavd;
int ret;
ret = security_compute_av_flags_raw(scon, tcon, tclass,
requested, &lavd);
if (ret == 0) {
avd->allowed = lavd.allowed;
avd->decided = lavd.decided;
avd->auditallow = lavd.auditallow;
avd->auditdeny = lavd.auditdeny;
avd->seqno = lavd.seqno;
/* NOTE:
* We should not return avd->flags via the interface
* due to the binary compatibility.
*/
}
return ret;
}
hidden_def(security_compute_av_raw) hidden_def(security_compute_av_raw)
int security_compute_av(security_context_t scon, int security_compute_av_flags(security_context_t scon,
security_context_t tcon, security_context_t tcon,
security_class_t tclass, security_class_t tclass,
access_vector_t requested, struct av_decision *avd) access_vector_t requested,
struct av_decision *avd)
{ {
int ret;
security_context_t rscon = scon; security_context_t rscon = scon;
security_context_t rtcon = tcon; security_context_t rtcon = tcon;
int ret;
if (selinux_trans_to_raw_context(scon, &rscon)) if (selinux_trans_to_raw_context(scon, &rscon))
return -1; return -1;
@ -83,8 +115,8 @@ int security_compute_av(security_context_t scon,
freecon(rscon); freecon(rscon);
return -1; return -1;
} }
ret = security_compute_av_flags_raw(rscon, rtcon, tclass,
ret = security_compute_av_raw(rscon, rtcon, tclass, requested, avd); requested, avd);
freecon(rscon); freecon(rscon);
freecon(rtcon); freecon(rtcon);
@ -92,4 +124,32 @@ int security_compute_av(security_context_t scon,
return ret; return ret;
} }
hidden_def(security_compute_av_flags)
int security_compute_av(security_context_t scon,
security_context_t tcon,
security_class_t tclass,
access_vector_t requested, struct av_decision *avd)
{
struct av_decision lavd;
int ret;
ret = security_compute_av_flags(scon, tcon, tclass,
requested, &lavd);
if (ret == 0)
{
avd->allowed = lavd.allowed;
avd->decided = lavd.decided;
avd->auditallow = lavd.auditallow;
avd->auditdeny = lavd.auditdeny;
avd->seqno = lavd.seqno;
/* NOTE:
* We should not return avd->flags via the interface
* due to the binary compatibility.
*/
}
return ret;
}
hidden_def(security_compute_av) hidden_def(security_compute_av)

View file

@ -16,6 +16,8 @@ hidden_proto(selinux_mkload_policy)
hidden_proto(security_canonicalize_context_raw) hidden_proto(security_canonicalize_context_raw)
hidden_proto(security_compute_av) hidden_proto(security_compute_av)
hidden_proto(security_compute_av_raw) hidden_proto(security_compute_av_raw)
hidden_proto(security_compute_av_flags)
hidden_proto(security_compute_av_flags_raw)
hidden_proto(security_compute_user) hidden_proto(security_compute_user)
hidden_proto(security_compute_user_raw) hidden_proto(security_compute_user_raw)
hidden_proto(security_compute_create) hidden_proto(security_compute_create)