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

Commit 5e8b2cba authored by Jeff Vander Stoep's avatar Jeff Vander Stoep Committed by Mark Salyzyn
Browse files

Revert "SELinux: per-command whitelisting of ioctls"



This reverts commit ba733f98.

Bug: 22846070
Change-Id: I4b5d9ce72996b7cf5b972dbcae22fbd6aff05149
Signed-off-by: default avatarJeff Vander Stoep <jeffv@google.com>
parent 69c79134
Loading
Loading
Loading
Loading
+18 −410
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/percpu.h>
#include <linux/list.h>
#include <net/sock.h>
#include <linux/un.h>
#include <net/af_unix.h>
@@ -49,7 +48,6 @@ struct avc_entry {
	u32			tsid;
	u16			tclass;
	struct av_decision	avd;
	struct avc_operation_node *ops_node;
};

struct avc_node {
@@ -66,16 +64,6 @@ struct avc_cache {
	u32			latest_notif;	/* latest revocation notification */
};

struct avc_operation_decision_node {
	struct operation_decision od;
	struct list_head od_list;
};

struct avc_operation_node {
	struct operation ops;
	struct list_head od_head; /* list of operation_decision_node */
};

struct avc_callback_node {
	int (*callback) (u32 event);
	u32 events;
@@ -92,9 +80,6 @@ DEFINE_PER_CPU(struct avc_cache_stats, avc_cache_stats) = { 0 };
static struct avc_cache avc_cache;
static struct avc_callback_node *avc_callbacks;
static struct kmem_cache *avc_node_cachep;
static struct kmem_cache *avc_operation_decision_node_cachep;
static struct kmem_cache *avc_operation_node_cachep;
static struct kmem_cache *avc_operation_perm_cachep;

static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass)
{
@@ -186,16 +171,6 @@ void __init avc_init(void)

	avc_node_cachep = kmem_cache_create("avc_node", sizeof(struct avc_node),
					     0, SLAB_PANIC, NULL);
	avc_operation_node_cachep = kmem_cache_create("avc_operation_node",
				sizeof(struct avc_operation_node),
				0, SLAB_PANIC, NULL);
	avc_operation_decision_node_cachep = kmem_cache_create(
				"avc_operation_decision_node",
				sizeof(struct avc_operation_decision_node),
				0, SLAB_PANIC, NULL);
	avc_operation_perm_cachep = kmem_cache_create("avc_operation_perm",
				sizeof(struct operation_perm),
				0, SLAB_PANIC, NULL);

	audit_log(current->audit_context, GFP_KERNEL, AUDIT_KERNEL, "AVC INITIALIZED\n");
}
@@ -230,269 +205,9 @@ int avc_get_hash_stats(char *page)
			 slots_used, AVC_CACHE_SLOTS, max_chain_len);
}

/*
 * using a linked list for operation_decision lookup because the list is
 * always small. i.e. less than 5, typically 1
 */
static struct operation_decision *avc_operation_lookup(u8 type,
					struct avc_operation_node *ops_node)
{
	struct avc_operation_decision_node *od_node;
	struct operation_decision *od = NULL;

	list_for_each_entry(od_node, &ops_node->od_head, od_list) {
		if (od_node->od.type != type)
			continue;
		od = &od_node->od;
		break;
	}
	return od;
}

static inline unsigned int avc_operation_has_perm(struct operation_decision *od,
						u16 cmd, u8 specified)
{
	unsigned int rc = 0;
	u8 num = cmd & 0xff;

	if ((specified == OPERATION_ALLOWED) &&
			(od->specified & OPERATION_ALLOWED))
		rc = security_operation_test(od->allowed->perms, num);
	else if ((specified == OPERATION_AUDITALLOW) &&
			(od->specified & OPERATION_AUDITALLOW))
		rc = security_operation_test(od->auditallow->perms, num);
	else if ((specified == OPERATION_DONTAUDIT) &&
			(od->specified & OPERATION_DONTAUDIT))
		rc = security_operation_test(od->dontaudit->perms, num);
	return rc;
}

static void avc_operation_allow_perm(struct avc_operation_node *node, u16 cmd)
{
	struct operation_decision *od;
	u8 type;
	u8 num;

	type = cmd >> 8;
	num = cmd & 0xff;
	security_operation_set(node->ops.type, type);
	od = avc_operation_lookup(type, node);
	if (od && od->allowed)
		security_operation_set(od->allowed->perms, num);
}

static void avc_operation_decision_free(
				struct avc_operation_decision_node *od_node)
{
	struct operation_decision *od;

	od = &od_node->od;
	if (od->allowed)
		kmem_cache_free(avc_operation_perm_cachep, od->allowed);
	if (od->auditallow)
		kmem_cache_free(avc_operation_perm_cachep, od->auditallow);
	if (od->dontaudit)
		kmem_cache_free(avc_operation_perm_cachep, od->dontaudit);
	kmem_cache_free(avc_operation_decision_node_cachep, od_node);
}

static void avc_operation_free(struct avc_operation_node *ops_node)
{
	struct avc_operation_decision_node *od_node;

	if (!ops_node)
		return;

	list_for_each_entry(od_node, &ops_node->od_head, od_list)
		avc_operation_decision_free(od_node);
	kmem_cache_free(avc_operation_node_cachep, ops_node);
}

static void avc_copy_operation_decision(struct operation_decision *dest,
					struct operation_decision *src)
{
	dest->type = src->type;
	dest->specified = src->specified;
	if (dest->specified & OPERATION_ALLOWED)
		memcpy(dest->allowed->perms, src->allowed->perms,
				sizeof(src->allowed->perms));
	if (dest->specified & OPERATION_AUDITALLOW)
		memcpy(dest->auditallow->perms, src->auditallow->perms,
				sizeof(src->auditallow->perms));
	if (dest->specified & OPERATION_DONTAUDIT)
		memcpy(dest->dontaudit->perms, src->dontaudit->perms,
				sizeof(src->dontaudit->perms));
}

/*
 * similar to avc_copy_operation_decision, but only copy decision
 * information relevant to this command
 */
static inline void avc_quick_copy_operation_decision(u16 cmd,
			struct operation_decision *dest,
			struct operation_decision *src)
{
	/*
	 * compute index of the u32 of the 256 bits (8 u32s) that contain this
	 * command permission
	 */
	u8 i = (0xff & cmd) >> 5;

	dest->specified = src->specified;
	if (dest->specified & OPERATION_ALLOWED)
		dest->allowed->perms[i] = src->allowed->perms[i];
	if (dest->specified & OPERATION_AUDITALLOW)
		dest->auditallow->perms[i] = src->auditallow->perms[i];
	if (dest->specified & OPERATION_DONTAUDIT)
		dest->dontaudit->perms[i] = src->dontaudit->perms[i];
}

static struct avc_operation_decision_node
		*avc_operation_decision_alloc(u8 specified)
{
	struct avc_operation_decision_node *node;
	struct operation_decision *od;

	node = kmem_cache_zalloc(avc_operation_decision_node_cachep,
				GFP_ATOMIC | __GFP_NOMEMALLOC);
	if (!node)
		return NULL;

	od = &node->od;
	if (specified & OPERATION_ALLOWED) {
		od->allowed = kmem_cache_zalloc(avc_operation_perm_cachep,
						GFP_ATOMIC | __GFP_NOMEMALLOC);
		if (!od->allowed)
			goto error;
	}
	if (specified & OPERATION_AUDITALLOW) {
		od->auditallow = kmem_cache_zalloc(avc_operation_perm_cachep,
						GFP_ATOMIC | __GFP_NOMEMALLOC);
		if (!od->auditallow)
			goto error;
	}
	if (specified & OPERATION_DONTAUDIT) {
		od->dontaudit = kmem_cache_zalloc(avc_operation_perm_cachep,
						GFP_ATOMIC | __GFP_NOMEMALLOC);
		if (!od->dontaudit)
			goto error;
	}
	return node;
error:
	avc_operation_decision_free(node);
	return NULL;
}

static int avc_add_operation(struct avc_node *node,
			struct operation_decision *od)
{
	struct avc_operation_decision_node *dest_od;

	node->ae.ops_node->ops.len++;
	dest_od = avc_operation_decision_alloc(od->specified);
	if (!dest_od)
		return -ENOMEM;
	avc_copy_operation_decision(&dest_od->od, od);
	list_add(&dest_od->od_list, &node->ae.ops_node->od_head);
	return 0;
}

static struct avc_operation_node *avc_operation_alloc(void)
{
	struct avc_operation_node *ops;

	ops = kmem_cache_zalloc(avc_operation_node_cachep,
				GFP_ATOMIC|__GFP_NOMEMALLOC);
	if (!ops)
		return ops;
	INIT_LIST_HEAD(&ops->od_head);
	return ops;
}

static int avc_operation_populate(struct avc_node *node,
				struct avc_operation_node *src)
{
	struct avc_operation_node *dest;
	struct avc_operation_decision_node *dest_od;
	struct avc_operation_decision_node *src_od;

	if (src->ops.len == 0)
		return 0;
	dest = avc_operation_alloc();
	if (!dest)
		return -ENOMEM;

	memcpy(dest->ops.type, &src->ops.type, sizeof(dest->ops.type));
	dest->ops.len = src->ops.len;

	/* for each source od allocate a destination od and copy */
	list_for_each_entry(src_od, &src->od_head, od_list) {
		dest_od = avc_operation_decision_alloc(src_od->od.specified);
		if (!dest_od)
			goto error;
		avc_copy_operation_decision(&dest_od->od, &src_od->od);
		list_add(&dest_od->od_list, &dest->od_head);
	}
	node->ae.ops_node = dest;
	return 0;
error:
	avc_operation_free(dest);
	return -ENOMEM;

}

static inline u32 avc_operation_audit_required(u32 requested,
					struct av_decision *avd,
					struct operation_decision *od,
					u16 cmd,
					int result,
					u32 *deniedp)
{
	u32 denied, audited;

	denied = requested & ~avd->allowed;
	if (unlikely(denied)) {
		audited = denied & avd->auditdeny;
		if (audited && od) {
			if (avc_operation_has_perm(od, cmd,
						OPERATION_DONTAUDIT))
				audited &= ~requested;
		}
	} else if (result) {
		audited = denied = requested;
	} else {
		audited = requested & avd->auditallow;
		if (audited && od) {
			if (!avc_operation_has_perm(od, cmd,
						OPERATION_AUDITALLOW))
				audited &= ~requested;
		}
	}

	*deniedp = denied;
	return audited;
}

static inline int avc_operation_audit(u32 ssid, u32 tsid, u16 tclass,
				u32 requested, struct av_decision *avd,
				struct operation_decision *od,
				u16 cmd, int result,
				struct common_audit_data *ad)
{
	u32 audited, denied;

	audited = avc_operation_audit_required(
			requested, avd, od, cmd, result, &denied);
	if (likely(!audited))
		return 0;
	return slow_avc_audit(ssid, tsid, tclass, requested,
			audited, denied, result, ad, 0);
}

static void avc_node_free(struct rcu_head *rhead)
{
	struct avc_node *node = container_of(rhead, struct avc_node, rhead);
	avc_operation_free(node->ae.ops_node);
	kmem_cache_free(avc_node_cachep, node);
	avc_cache_stats_incr(frees);
}
@@ -506,7 +221,6 @@ static void avc_node_delete(struct avc_node *node)

static void avc_node_kill(struct avc_node *node)
{
	avc_operation_free(node->ae.ops_node);
	kmem_cache_free(avc_node_cachep, node);
	avc_cache_stats_incr(frees);
	atomic_dec(&avc_cache.active_nodes);
@@ -653,7 +367,6 @@ static int avc_latest_notif_update(int seqno, int is_insert)
 * @tsid: target security identifier
 * @tclass: target security class
 * @avd: resulting av decision
 * @ops: resulting operation decisions
 *
 * Insert an AVC entry for the SID pair
 * (@ssid, @tsid) and class @tclass.
@@ -665,9 +378,7 @@ static int avc_latest_notif_update(int seqno, int is_insert)
 * the access vectors into a cache entry, returns
 * avc_node inserted. Otherwise, this function returns NULL.
 */
static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass,
				struct av_decision *avd,
				struct avc_operation_node *ops_node)
static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct av_decision *avd)
{
	struct avc_node *pos, *node = NULL;
	int hvalue;
@@ -680,15 +391,10 @@ static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass,
	if (node) {
		struct hlist_head *head;
		spinlock_t *lock;
		int rc = 0;

		hvalue = avc_hash(ssid, tsid, tclass);
		avc_node_populate(node, ssid, tsid, tclass, avd);
		rc = avc_operation_populate(node, ops_node);
		if (rc) {
			kmem_cache_free(avc_node_cachep, node);
			return NULL;
		}

		head = &avc_cache.slots[hvalue];
		lock = &avc_cache.slots_lock[hvalue];

@@ -822,17 +528,14 @@ static inline int avc_sidcmp(u32 x, u32 y)
 * @perms : Permission mask bits
 * @ssid,@tsid,@tclass : identifier of an AVC entry
 * @seqno : sequence number when decision was made
 * @od: operation_decision to be added to the node
 *
 * if a valid AVC entry doesn't exist,this function returns -ENOENT.
 * if kmalloc() called internal returns NULL, this function returns -ENOMEM.
 * otherwise, this function updates the AVC entry. The original AVC-entry object
 * will release later by RCU.
 */
static int avc_update_node(u32 event, u32 perms, u16 cmd, u32 ssid, u32 tsid,
			u16 tclass, u32 seqno,
			struct operation_decision *od,
			u32 flags)
static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass,
			   u32 seqno)
{
	int hvalue, rc = 0;
	unsigned long flag;
@@ -876,19 +579,9 @@ static int avc_update_node(u32 event, u32 perms, u16 cmd, u32 ssid, u32 tsid,

	avc_node_populate(node, ssid, tsid, tclass, &orig->ae.avd);

	if (orig->ae.ops_node) {
		rc = avc_operation_populate(node, orig->ae.ops_node);
		if (rc) {
			kmem_cache_free(avc_node_cachep, node);
			goto out_unlock;
		}
	}

	switch (event) {
	case AVC_CALLBACK_GRANT:
		node->ae.avd.allowed |= perms;
		if (node->ae.ops_node && (flags & AVC_OPERATION_CMD))
			avc_operation_allow_perm(node->ae.ops_node, cmd);
		break;
	case AVC_CALLBACK_TRY_REVOKE:
	case AVC_CALLBACK_REVOKE:
@@ -906,9 +599,6 @@ static int avc_update_node(u32 event, u32 perms, u16 cmd, u32 ssid, u32 tsid,
	case AVC_CALLBACK_AUDITDENY_DISABLE:
		node->ae.avd.auditdeny &= ~perms;
		break;
	case AVC_CALLBACK_ADD_OPERATION:
		avc_add_operation(node, od);
		break;
	}
	avc_node_replace(node, orig);
out_unlock:
@@ -980,19 +670,17 @@ int avc_ss_reset(u32 seqno)
 * results in a bigger stack frame.
 */
static noinline struct avc_node *avc_compute_av(u32 ssid, u32 tsid,
			 u16 tclass, struct av_decision *avd,
			 struct avc_operation_node *ops_node)
			 u16 tclass, struct av_decision *avd)
{
	rcu_read_unlock();
	INIT_LIST_HEAD(&ops_node->od_head);
	security_compute_av(ssid, tsid, tclass, avd, &ops_node->ops);
	security_compute_av(ssid, tsid, tclass, avd);
	rcu_read_lock();
	return avc_insert(ssid, tsid, tclass, avd, ops_node);
	return avc_insert(ssid, tsid, tclass, avd);
}

static noinline int avc_denied(u32 ssid, u32 tsid,
			 u16 tclass, u32 requested,
				u16 cmd, unsigned flags,
			 unsigned flags,
			 struct av_decision *avd)
{
	if (flags & AVC_STRICT)
@@ -1001,92 +689,11 @@ static noinline int avc_denied(u32 ssid, u32 tsid,
	if (selinux_enforcing && !(avd->flags & AVD_FLAGS_PERMISSIVE))
		return -EACCES;

	avc_update_node(AVC_CALLBACK_GRANT, requested, cmd, ssid,
				tsid, tclass, avd->seqno, NULL, flags);
	avc_update_node(AVC_CALLBACK_GRANT, requested, ssid,
				tsid, tclass, avd->seqno);
	return 0;
}

/*
 * ioctl commands are comprised of four fields, direction, size, type, and
 * number. The avc operation logic filters based on two of them:
 *
 * type: or code, typically unique to each driver
 * number: or function
 *
 * For example, 0x89 is a socket type, and number 0x27 is the get hardware
 * address function.
 */
int avc_has_operation(u32 ssid, u32 tsid, u16 tclass, u32 requested,
			u16 cmd, struct common_audit_data *ad)
{
	struct avc_node *node;
	struct av_decision avd;
	u32 denied;
	struct operation_decision *od = NULL;
	struct operation_decision od_local;
	struct operation_perm allowed;
	struct operation_perm auditallow;
	struct operation_perm dontaudit;
	struct avc_operation_node local_ops_node;
	struct avc_operation_node *ops_node;
	u8 type = cmd >> 8;
	int rc = 0, rc2;

	ops_node = &local_ops_node;
	BUG_ON(!requested);

	rcu_read_lock();

	node = avc_lookup(ssid, tsid, tclass);
	if (unlikely(!node)) {
		node = avc_compute_av(ssid, tsid, tclass, &avd, ops_node);
	} else {
		memcpy(&avd, &node->ae.avd, sizeof(avd));
		ops_node = node->ae.ops_node;
	}
	/* if operations are not defined, only consider av_decision */
	if (!ops_node || !ops_node->ops.len)
		goto decision;

	od_local.allowed = &allowed;
	od_local.auditallow = &auditallow;
	od_local.dontaudit = &dontaudit;

	/* lookup operation decision */
	od = avc_operation_lookup(type, ops_node);
	if (unlikely(!od)) {
		/* Compute operation decision if type is flagged */
		if (!security_operation_test(ops_node->ops.type, type)) {
			avd.allowed &= ~requested;
			goto decision;
		}
		rcu_read_unlock();
		security_compute_operation(ssid, tsid, tclass, type, &od_local);
		rcu_read_lock();
		avc_update_node(AVC_CALLBACK_ADD_OPERATION, requested, cmd,
				ssid, tsid, tclass, avd.seqno, &od_local, 0);
	} else {
		avc_quick_copy_operation_decision(cmd, &od_local, od);
	}
	od = &od_local;

	if (!avc_operation_has_perm(od, cmd, OPERATION_ALLOWED))
		avd.allowed &= ~requested;

decision:
	denied = requested & ~(avd.allowed);
	if (unlikely(denied))
		rc = avc_denied(ssid, tsid, tclass, requested, cmd,
				AVC_OPERATION_CMD, &avd);

	rcu_read_unlock();

	rc2 = avc_operation_audit(ssid, tsid, tclass, requested,
			&avd, od, cmd, rc, ad);
	if (rc2)
		return rc2;
	return rc;
}

/**
 * avc_has_perm_noaudit - Check permissions but perform no auditing.
@@ -1114,7 +721,6 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
			 struct av_decision *avd)
{
	struct avc_node *node;
	struct avc_operation_node ops_node;
	int rc = 0;
	u32 denied;

@@ -1123,14 +729,16 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
	rcu_read_lock();

	node = avc_lookup(ssid, tsid, tclass);
	if (unlikely(!node))
		node = avc_compute_av(ssid, tsid, tclass, avd, &ops_node);
	else
	if (unlikely(!node)) {
		node = avc_compute_av(ssid, tsid, tclass, avd);
	} else {
		memcpy(avd, &node->ae.avd, sizeof(*avd));
		avd = &node->ae.avd;
	}

	denied = requested & ~(avd->allowed);
	if (unlikely(denied))
		rc = avc_denied(ssid, tsid, tclass, requested, 0, flags, avd);
		rc = avc_denied(ssid, tsid, tclass, requested, flags, avd);

	rcu_read_unlock();
	return rc;
+1 −39
Original line number Diff line number Diff line
@@ -3238,44 +3238,6 @@ static void selinux_file_free_security(struct file *file)
	file_free_security(file);
}

/*
 * Check whether a task has the ioctl permission and cmd
 * operation to an inode.
 */
int ioctl_has_perm(const struct cred *cred, struct file *file,
		u32 requested, u16 cmd)
{
	struct common_audit_data ad;
	struct file_security_struct *fsec = file->f_security;
	struct inode *inode = file_inode(file);
	struct inode_security_struct *isec = inode->i_security;
	struct lsm_ioctlop_audit ioctl;
	u32 ssid = cred_sid(cred);
	int rc;

	ad.type = LSM_AUDIT_DATA_IOCTL_OP;
	ad.u.op = &ioctl;
	ad.u.op->cmd = cmd;
	ad.u.op->path = file->f_path;

	if (ssid != fsec->sid) {
		rc = avc_has_perm(ssid, fsec->sid,
				SECCLASS_FD,
				FD__USE,
				&ad);
		if (rc)
			goto out;
	}

	if (unlikely(IS_PRIVATE(inode)))
		return 0;

	rc = avc_has_operation(ssid, isec->sid, isec->sclass,
			requested, cmd, &ad);
out:
	return rc;
}

static int selinux_file_ioctl(struct file *file, unsigned int cmd,
			      unsigned long arg)
{
@@ -3318,7 +3280,7 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd,
	 * to the file's ioctl() function.
	 */
	default:
		error = ioctl_has_perm(cred, file, FILE__IOCTL, (u16) cmd);
		error = file_has_perm(cred, file, FILE__IOCTL);
	}
	return error;
}
+0 −5
Original line number Diff line number Diff line
@@ -142,7 +142,6 @@ static inline int avc_audit(u32 ssid, u32 tsid,
}

#define AVC_STRICT 1 /* Ignore permissive mode. */
#define AVC_OPERATION_CMD 2	/* ignore command when updating operations */
int avc_has_perm_noaudit(u32 ssid, u32 tsid,
			 u16 tclass, u32 requested,
			 unsigned flags,
@@ -152,9 +151,6 @@ int avc_has_perm(u32 ssid, u32 tsid,
		 u16 tclass, u32 requested,
		 struct common_audit_data *auditdata);

int avc_has_operation(u32 ssid, u32 tsid, u16 tclass, u32 requested,
		u16 cmd, struct common_audit_data *ad);

u32 avc_policy_seqno(void);

#define AVC_CALLBACK_GRANT		1
@@ -165,7 +161,6 @@ u32 avc_policy_seqno(void);
#define AVC_CALLBACK_AUDITALLOW_DISABLE	32
#define AVC_CALLBACK_AUDITDENY_ENABLE	64
#define AVC_CALLBACK_AUDITDENY_DISABLE	128
#define AVC_CALLBACK_ADD_OPERATION	256

int avc_add_callback(int (*callback)(u32 event), u32 events);

+2 −32
Original line number Diff line number Diff line
@@ -35,14 +35,13 @@
#define POLICYDB_VERSION_NEW_OBJECT_DEFAULTS	27
#define POLICYDB_VERSION_DEFAULT_TYPE	28
#define POLICYDB_VERSION_CONSTRAINT_NAMES	29
#define POLICYDB_VERSION_IOCTL_OPERATIONS	30

/* Range of policy versions we understand*/
#define POLICYDB_VERSION_MIN   POLICYDB_VERSION_BASE
#ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX
#define POLICYDB_VERSION_MAX	CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE
#else
#define POLICYDB_VERSION_MAX	POLICYDB_VERSION_IOCTL_OPERATIONS
#define POLICYDB_VERSION_MAX	POLICYDB_VERSION_CONSTRAINT_NAMES
#endif

/* Mask for just the mount related flags */
@@ -110,40 +109,11 @@ struct av_decision {
	u32 flags;
};

#define security_operation_set(perms, x) (perms[x >> 5] |= 1 << (x & 0x1f))
#define security_operation_test(perms, x) (1 & (perms[x >> 5] >> (x & 0x1f)))

struct operation_perm {
	u32 perms[8];
};

struct operation_decision {
	u8 type;
	u8 specified;
	struct operation_perm *allowed;
	struct operation_perm *auditallow;
	struct operation_perm *dontaudit;
};

#define OPERATION_ALLOWED 1
#define OPERATION_AUDITALLOW 2
#define OPERATION_DONTAUDIT 4
#define OPERATION_ALL (OPERATION_ALLOWED | OPERATION_AUDITALLOW |\
			OPERATION_DONTAUDIT)
struct operation {
	u16 len;	/* length of operation decision chain */
	u32 type[8];	/* 256 types */
};

/* definitions of av_decision.flags */
#define AVD_FLAGS_PERMISSIVE	0x0001

void security_compute_av(u32 ssid, u32 tsid,
			 u16 tclass, struct av_decision *avd,
			 struct operation *ops);

void security_compute_operation(u32 ssid, u32 tsid, u16 tclass,
			 u8 type, struct operation_decision *od);
			 u16 tclass, struct av_decision *avd);

void security_compute_av_user(u32 ssid, u32 tsid,
			     u16 tclass, struct av_decision *avd);
+15 −76
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@
#include "policydb.h"

static struct kmem_cache *avtab_node_cachep;
static struct kmem_cache *avtab_operation_cachep;

static inline int avtab_hash(struct avtab_key *keyp, u16 mask)
{
@@ -38,24 +37,11 @@ avtab_insert_node(struct avtab *h, int hvalue,
		  struct avtab_key *key, struct avtab_datum *datum)
{
	struct avtab_node *newnode;
	struct avtab_operation *ops;
	newnode = kmem_cache_zalloc(avtab_node_cachep, GFP_KERNEL);
	if (newnode == NULL)
		return NULL;
	newnode->key = *key;

	if (key->specified & AVTAB_OP) {
		ops = kmem_cache_zalloc(avtab_operation_cachep, GFP_KERNEL);
		if (ops == NULL) {
			kmem_cache_free(avtab_node_cachep, newnode);
			return NULL;
		}
		*ops = *(datum->u.ops);
		newnode->datum.u.ops = ops;
	} else {
		newnode->datum.u.data = datum->u.data;
	}

	newnode->datum = *datum;
	if (prev) {
		newnode->next = prev->next;
		prev->next = newnode;
@@ -84,11 +70,8 @@ static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_dat
		if (key->source_type == cur->key.source_type &&
		    key->target_type == cur->key.target_type &&
		    key->target_class == cur->key.target_class &&
		    (specified & cur->key.specified)) {
			if (specified & AVTAB_OPNUM)
				break;
		    (specified & cur->key.specified))
			return -EEXIST;
		}
		if (key->source_type < cur->key.source_type)
			break;
		if (key->source_type == cur->key.source_type &&
@@ -249,9 +232,6 @@ void avtab_destroy(struct avtab *h)
		while (cur) {
			temp = cur;
			cur = cur->next;
			if (temp->key.specified & AVTAB_OP)
				kmem_cache_free(avtab_operation_cachep,
							temp->datum.u.ops);
			kmem_cache_free(avtab_node_cachep, temp);
		}
		h->htable[i] = NULL;
@@ -340,13 +320,7 @@ static uint16_t spec_order[] = {
	AVTAB_AUDITALLOW,
	AVTAB_TRANSITION,
	AVTAB_CHANGE,
	AVTAB_MEMBER,
	AVTAB_OPNUM_ALLOWED,
	AVTAB_OPNUM_AUDITALLOW,
	AVTAB_OPNUM_DONTAUDIT,
	AVTAB_OPTYPE_ALLOWED,
	AVTAB_OPTYPE_AUDITALLOW,
	AVTAB_OPTYPE_DONTAUDIT
	AVTAB_MEMBER
};

int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
@@ -356,11 +330,10 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
{
	__le16 buf16[4];
	u16 enabled;
	__le32 buf32[7];
	u32 items, items2, val, vers = pol->policyvers;
	struct avtab_key key;
	struct avtab_datum datum;
	struct avtab_operation ops;
	__le32 buf32[ARRAY_SIZE(ops.op.perms)];
	int i, rc;
	unsigned set;

@@ -417,15 +390,11 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
			printk(KERN_ERR "SELinux: avtab: entry has both access vectors and types\n");
			return -EINVAL;
		}
		if (val & AVTAB_OP) {
			printk(KERN_ERR "SELinux: avtab: entry has operations\n");
			return -EINVAL;
		}

		for (i = 0; i < ARRAY_SIZE(spec_order); i++) {
			if (val & spec_order[i]) {
				key.specified = spec_order[i] | enabled;
				datum.u.data = le32_to_cpu(buf32[items++]);
				datum.data = le32_to_cpu(buf32[items++]);
				rc = insertf(a, &key, &datum, p);
				if (rc)
					return rc;
@@ -444,6 +413,7 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
		printk(KERN_ERR "SELinux: avtab: truncated entry\n");
		return rc;
	}

	items = 0;
	key.source_type = le16_to_cpu(buf16[items++]);
	key.target_type = le16_to_cpu(buf16[items++]);
@@ -467,32 +437,14 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
		return -EINVAL;
	}

	if ((vers < POLICYDB_VERSION_IOCTL_OPERATIONS)
			|| !(key.specified & AVTAB_OP)) {
	rc = next_entry(buf32, fp, sizeof(u32));
	if (rc) {
		printk(KERN_ERR "SELinux: avtab: truncated entry\n");
		return rc;
	}
		datum.u.data = le32_to_cpu(*buf32);
	} else {
		memset(&ops, 0, sizeof(struct avtab_operation));
		rc = next_entry(&ops.type, fp, sizeof(u8));
		if (rc) {
			printk(KERN_ERR "SELinux: avtab: truncated entry\n");
			return rc;
		}
		rc = next_entry(buf32, fp, sizeof(u32)*ARRAY_SIZE(ops.op.perms));
		if (rc) {
			printk(KERN_ERR "SELinux: avtab: truncated entry\n");
			return rc;
		}
		for (i = 0; i < ARRAY_SIZE(ops.op.perms); i++)
			ops.op.perms[i] = le32_to_cpu(buf32[i]);
		datum.u.ops = &ops;
	}
	datum.data = le32_to_cpu(*buf32);
	if ((key.specified & AVTAB_TYPE) &&
	    !policydb_type_isvalid(pol, datum.u.data)) {
	    !policydb_type_isvalid(pol, datum.data)) {
		printk(KERN_ERR "SELinux: avtab: invalid type\n");
		return -EINVAL;
	}
@@ -552,9 +504,8 @@ bad:
int avtab_write_item(struct policydb *p, struct avtab_node *cur, void *fp)
{
	__le16 buf16[4];
	__le32 buf32[ARRAY_SIZE(cur->datum.u.ops->op.perms)];
	__le32 buf32[1];
	int rc;
	unsigned int i;

	buf16[0] = cpu_to_le16(cur->key.source_type);
	buf16[1] = cpu_to_le16(cur->key.target_type);
@@ -563,16 +514,8 @@ int avtab_write_item(struct policydb *p, struct avtab_node *cur, void *fp)
	rc = put_entry(buf16, sizeof(u16), 4, fp);
	if (rc)
		return rc;

	if (cur->key.specified & AVTAB_OP) {
		for (i = 0; i < ARRAY_SIZE(cur->datum.u.ops->op.perms); i++)
			buf32[i] = cpu_to_le32(cur->datum.u.ops->op.perms[i]);
		rc = put_entry(buf32, sizeof(u32),
				ARRAY_SIZE(cur->datum.u.ops->op.perms), fp);
	} else {
		buf32[0] = cpu_to_le32(cur->datum.u.data);
	buf32[0] = cpu_to_le32(cur->datum.data);
	rc = put_entry(buf32, sizeof(u32), 1, fp);
	}
	if (rc)
		return rc;
	return 0;
@@ -605,13 +548,9 @@ void avtab_cache_init(void)
	avtab_node_cachep = kmem_cache_create("avtab_node",
					      sizeof(struct avtab_node),
					      0, SLAB_PANIC, NULL);
	avtab_operation_cachep = kmem_cache_create("avtab_operation",
					      sizeof(struct avtab_operation),
					      0, SLAB_PANIC, NULL);
}

void avtab_cache_destroy(void)
{
	kmem_cache_destroy(avtab_node_cachep);
	kmem_cache_destroy(avtab_operation_cachep);
}
Loading