Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 19439d05 authored by Stephen Smalley's avatar Stephen Smalley Committed by James Morris
Browse files

selinux: change the handling of unknown classes



If allow_unknown==deny, SELinux treats an undefined kernel security
class as an error condition rather than as a typical permission denial
and thus does not allow permissions on undefined classes even when in
permissive mode.  Change the SELinux logic so that this case is handled
as a typical permission denial, subject to the usual permissive mode and
permissive domain handling.

Also drop the 'requested' argument from security_compute_av() and
helpers as it is a legacy of the original security server interface and
is unused.

Changes:
- Handle permissive domains consistently by moving up the test for a
permissive domain.
- Make security_compute_av_user() consistent with security_compute_av();
the only difference now is that security_compute_av() performs mapping
between the kernel-private class and permission indices and the policy
values.  In the userspace case, this mapping is handled by libselinux.
- Moved avd_init inside the policy lock.

Based in part on a patch by Paul Moore <paul.moore@hp.com>.

Reported-by: default avatarAndrew Worsley <amworsley@gmail.com>
Signed-off-by: default avatarStephen D. Smalley <sds@tycho.nsa.gov>
Reviewed-by: default avatarPaul Moore <paul.moore@hp.com>
Signed-off-by: default avatarJames Morris <jmorris@namei.org>
parent 8d952504
Loading
Loading
Loading
Loading
+1 −4
Original line number Diff line number Diff line
@@ -746,9 +746,7 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
		else
			avd = &avd_entry;

		rc = security_compute_av(ssid, tsid, tclass, requested, avd);
		if (rc)
			goto out;
		security_compute_av(ssid, tsid, tclass, avd);
		rcu_read_lock();
		node = avc_insert(ssid, tsid, tclass, avd);
	} else {
@@ -770,7 +768,6 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
	}

	rcu_read_unlock();
out:
	return rc;
}

+4 −6
Original line number Diff line number Diff line
@@ -96,13 +96,11 @@ struct av_decision {
/* definitions of av_decision.flags */
#define AVD_FLAGS_PERMISSIVE	0x0001

int security_compute_av(u32 ssid, u32 tsid,
			u16 tclass, u32 requested,
			struct av_decision *avd);
void security_compute_av(u32 ssid, u32 tsid,
			 u16 tclass, struct av_decision *avd);

int security_compute_av_user(u32 ssid, u32 tsid,
			     u16 tclass, u32 requested,
			     struct av_decision *avd);
void security_compute_av_user(u32 ssid, u32 tsid,
			     u16 tclass, struct av_decision *avd);

int security_transition_sid(u32 ssid, u32 tsid,
			    u16 tclass, u32 *out_sid);
+2 −5
Original line number Diff line number Diff line
@@ -494,7 +494,6 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size)
	char *scon, *tcon;
	u32 ssid, tsid;
	u16 tclass;
	u32 req;
	struct av_decision avd;
	ssize_t length;

@@ -512,7 +511,7 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size)
		goto out;

	length = -EINVAL;
	if (sscanf(buf, "%s %s %hu %x", scon, tcon, &tclass, &req) != 4)
	if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)
		goto out2;

	length = security_context_to_sid(scon, strlen(scon)+1, &ssid);
@@ -522,9 +521,7 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size)
	if (length < 0)
		goto out2;

	length = security_compute_av_user(ssid, tsid, tclass, req, &avd);
	if (length < 0)
		goto out2;
	security_compute_av_user(ssid, tsid, tclass, &avd);

	length = scnprintf(buf, SIMPLE_TRANSACTION_LIMIT,
			  "%x %x %x %x %u %x",
+81 −105
Original line number Diff line number Diff line
@@ -87,10 +87,9 @@ static u32 latest_granting;
static int context_struct_to_string(struct context *context, char **scontext,
				    u32 *scontext_len);

static int context_struct_compute_av(struct context *scontext,
static void context_struct_compute_av(struct context *scontext,
				      struct context *tcontext,
				      u16 tclass,
				     u32 requested,
				      struct av_decision *avd);

struct selinux_mapping {
@@ -196,23 +195,6 @@ static u16 unmap_class(u16 tclass)
	return tclass;
}

static u32 unmap_perm(u16 tclass, u32 tperm)
{
	if (tclass < current_mapping_size) {
		unsigned i;
		u32 kperm = 0;

		for (i = 0; i < current_mapping[tclass].num_perms; i++)
			if (tperm & (1<<i)) {
				kperm |= current_mapping[tclass].perms[i];
				tperm &= ~(1<<i);
			}
		return kperm;
	}

	return tperm;
}

static void map_decision(u16 tclass, struct av_decision *avd,
			 int allow_unknown)
{
@@ -532,7 +514,6 @@ static void security_dump_masked_av(struct context *scontext,
static void type_attribute_bounds_av(struct context *scontext,
				     struct context *tcontext,
				     u16 tclass,
				     u32 requested,
				     struct av_decision *avd)
{
	struct context lo_scontext;
@@ -553,7 +534,6 @@ static void type_attribute_bounds_av(struct context *scontext,
		context_struct_compute_av(&lo_scontext,
					  tcontext,
					  tclass,
					  requested,
					  &lo_avd);
		if ((lo_avd.allowed & avd->allowed) == avd->allowed)
			return;		/* no masked permission */
@@ -569,7 +549,6 @@ static void type_attribute_bounds_av(struct context *scontext,
		context_struct_compute_av(scontext,
					  &lo_tcontext,
					  tclass,
					  requested,
					  &lo_avd);
		if ((lo_avd.allowed & avd->allowed) == avd->allowed)
			return;		/* no masked permission */
@@ -586,7 +565,6 @@ static void type_attribute_bounds_av(struct context *scontext,
		context_struct_compute_av(&lo_scontext,
					  &lo_tcontext,
					  tclass,
					  requested,
					  &lo_avd);
		if ((lo_avd.allowed & avd->allowed) == avd->allowed)
			return;		/* no masked permission */
@@ -607,10 +585,9 @@ static void type_attribute_bounds_av(struct context *scontext,
 * Compute access vectors based on a context structure pair for
 * the permissions in a particular class.
 */
static int context_struct_compute_av(struct context *scontext,
static void context_struct_compute_av(struct context *scontext,
				      struct context *tcontext,
				      u16 tclass,
				     u32 requested,
				      struct av_decision *avd)
{
	struct constraint_node *constraint;
@@ -622,19 +599,14 @@ static int context_struct_compute_av(struct context *scontext,
	struct ebitmap_node *snode, *tnode;
	unsigned int i, j;

	/*
	 * Initialize the access vectors to the default values.
	 */
	avd->allowed = 0;
	avd->auditallow = 0;
	avd->auditdeny = 0xffffffff;
	avd->seqno = latest_granting;
	avd->flags = 0;

	if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) {
		if (printk_ratelimit())
			printk(KERN_WARNING "SELinux:  Invalid class %hu\n", tclass);
		return -EINVAL;
		return;
	}

	tclass_datum = policydb.class_val_to_struct[tclass - 1];
@@ -705,9 +677,7 @@ static int context_struct_compute_av(struct context *scontext,
	 * permission and notice it to userspace via audit.
	 */
	type_attribute_bounds_av(scontext, tcontext,
				 tclass, requested, avd);

	return 0;
				 tclass, avd);
}

static int security_validtrans_handle_fail(struct context *ocontext,
@@ -886,110 +856,116 @@ int security_bounded_transition(u32 old_sid, u32 new_sid)
	return rc;
}


static int security_compute_av_core(u32 ssid,
				    u32 tsid,
				    u16 tclass,
				    u32 requested,
				    struct av_decision *avd)
static void avd_init(struct av_decision *avd)
{
	struct context *scontext = NULL, *tcontext = NULL;
	int rc = 0;

	scontext = sidtab_search(&sidtab, ssid);
	if (!scontext) {
		printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
		       __func__, ssid);
		return -EINVAL;
	}
	tcontext = sidtab_search(&sidtab, tsid);
	if (!tcontext) {
		printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
		       __func__, tsid);
		return -EINVAL;
	avd->allowed = 0;
	avd->auditallow = 0;
	avd->auditdeny = 0xffffffff;
	avd->seqno = latest_granting;
	avd->flags = 0;
}

	rc = context_struct_compute_av(scontext, tcontext, tclass,
				       requested, avd);

	/* permissive domain? */
	if (ebitmap_get_bit(&policydb.permissive_map, scontext->type))
		avd->flags |= AVD_FLAGS_PERMISSIVE;

	return rc;
}

/**
 * security_compute_av - Compute access vector decisions.
 * @ssid: source security identifier
 * @tsid: target security identifier
 * @tclass: target security class
 * @requested: requested permissions
 * @avd: access vector decisions
 *
 * Compute a set of access vector decisions based on the
 * SID pair (@ssid, @tsid) for the permissions in @tclass.
 * Return -%EINVAL if any of the parameters are invalid or %0
 * if the access vector decisions were computed successfully.
 */
int security_compute_av(u32 ssid,
void security_compute_av(u32 ssid,
			 u32 tsid,
			 u16 orig_tclass,
			u32 orig_requested,
			 struct av_decision *avd)
{
	u16 tclass;
	u32 requested;
	int rc;
	struct context *scontext = NULL, *tcontext = NULL;

	read_lock(&policy_rwlock);

	avd_init(avd);
	if (!ss_initialized)
		goto allow;

	requested = unmap_perm(orig_tclass, orig_requested);
	scontext = sidtab_search(&sidtab, ssid);
	if (!scontext) {
		printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
		       __func__, ssid);
		goto out;
	}

	/* permissive domain? */
	if (ebitmap_get_bit(&policydb.permissive_map, scontext->type))
		avd->flags |= AVD_FLAGS_PERMISSIVE;

	tcontext = sidtab_search(&sidtab, tsid);
	if (!tcontext) {
		printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
		       __func__, tsid);
		goto out;
	}

	tclass = unmap_class(orig_tclass);
	if (unlikely(orig_tclass && !tclass)) {
		if (policydb.allow_unknown)
			goto allow;
		rc = -EINVAL;
		goto out;
	}
	rc = security_compute_av_core(ssid, tsid, tclass, requested, avd);
	context_struct_compute_av(scontext, tcontext, tclass, avd);
	map_decision(orig_tclass, avd, policydb.allow_unknown);
out:
	read_unlock(&policy_rwlock);
	return rc;
	return;
allow:
	avd->allowed = 0xffffffff;
	avd->auditallow = 0;
	avd->auditdeny = 0xffffffff;
	avd->seqno = latest_granting;
	avd->flags = 0;
	rc = 0;
	goto out;
}

int security_compute_av_user(u32 ssid,
void security_compute_av_user(u32 ssid,
			      u32 tsid,
			      u16 tclass,
			     u32 requested,
			      struct av_decision *avd)
{
	int rc;
	struct context *scontext = NULL, *tcontext = NULL;

	if (!ss_initialized) {
		avd->allowed = 0xffffffff;
		avd->auditallow = 0;
		avd->auditdeny = 0xffffffff;
		avd->seqno = latest_granting;
		return 0;
	read_lock(&policy_rwlock);
	avd_init(avd);
	if (!ss_initialized)
		goto allow;

	scontext = sidtab_search(&sidtab, ssid);
	if (!scontext) {
		printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
		       __func__, ssid);
		goto out;
	}

	read_lock(&policy_rwlock);
	rc = security_compute_av_core(ssid, tsid, tclass, requested, avd);
	/* permissive domain? */
	if (ebitmap_get_bit(&policydb.permissive_map, scontext->type))
		avd->flags |= AVD_FLAGS_PERMISSIVE;

	tcontext = sidtab_search(&sidtab, tsid);
	if (!tcontext) {
		printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
		       __func__, tsid);
		goto out;
	}

	if (unlikely(!tclass)) {
		if (policydb.allow_unknown)
			goto allow;
		goto out;
	}

	context_struct_compute_av(scontext, tcontext, tclass, avd);
 out:
	read_unlock(&policy_rwlock);
	return rc;
	return;
allow:
	avd->allowed = 0xffffffff;
	goto out;
}

/*