Commit 09a0fa92 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull selinux fix from Paul Moore:
 "A single SELinux patch to address a problem with a single domain using
  multiple xperm classes"

* tag 'selinux-pr-20250107' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux:
  selinux: match extended permissions to their base permissions
parents fbfd64d2 5e7f0efd
Loading
Loading
Loading
Loading
+34 −27
Original line number Diff line number Diff line
@@ -174,13 +174,15 @@ int avc_get_hash_stats(char *page)
 * using a linked list for extended_perms_decision lookup because the list is
 * always small. i.e. less than 5, typically 1
 */
static struct extended_perms_decision *avc_xperms_decision_lookup(u8 driver,
static struct extended_perms_decision *
avc_xperms_decision_lookup(u8 driver, u8 base_perm,
			   struct avc_xperms_node *xp_node)
{
	struct avc_xperms_decision_node *xpd_node;

	list_for_each_entry(xpd_node, &xp_node->xpd_head, xpd_list) {
		if (xpd_node->xpd.driver == driver)
		if (xpd_node->xpd.driver == driver &&
		    xpd_node->xpd.base_perm == base_perm)
			return &xpd_node->xpd;
	}
	return NULL;
@@ -205,11 +207,12 @@ avc_xperms_has_perm(struct extended_perms_decision *xpd,
}

static void avc_xperms_allow_perm(struct avc_xperms_node *xp_node,
				u8 driver, u8 perm)
				  u8 driver, u8 base_perm, u8 perm)
{
	struct extended_perms_decision *xpd;
	security_xperm_set(xp_node->xp.drivers.p, driver);
	xpd = avc_xperms_decision_lookup(driver, xp_node);
	xp_node->xp.base_perms |= base_perm;
	xpd = avc_xperms_decision_lookup(driver, base_perm, xp_node);
	if (xpd && xpd->allowed)
		security_xperm_set(xpd->allowed->p, perm);
}
@@ -245,6 +248,7 @@ static void avc_xperms_free(struct avc_xperms_node *xp_node)
static void avc_copy_xperms_decision(struct extended_perms_decision *dest,
					struct extended_perms_decision *src)
{
	dest->base_perm = src->base_perm;
	dest->driver = src->driver;
	dest->used = src->used;
	if (dest->used & XPERMS_ALLOWED)
@@ -272,6 +276,7 @@ static inline void avc_quick_copy_xperms_decision(u8 perm,
	 */
	u8 i = perm >> 5;

	dest->base_perm = src->base_perm;
	dest->used = src->used;
	if (dest->used & XPERMS_ALLOWED)
		dest->allowed->p[i] = src->allowed->p[i];
@@ -357,6 +362,7 @@ static int avc_xperms_populate(struct avc_node *node,

	memcpy(dest->xp.drivers.p, src->xp.drivers.p, sizeof(dest->xp.drivers.p));
	dest->xp.len = src->xp.len;
	dest->xp.base_perms = src->xp.base_perms;

	/* for each source xpd allocate a destination xpd and copy */
	list_for_each_entry(src_xpd, &src->xpd_head, xpd_list) {
@@ -807,6 +813,7 @@ int __init avc_add_callback(int (*callback)(u32 event), u32 events)
 * @event : Updating event
 * @perms : Permission mask bits
 * @driver: xperm driver information
 * @base_perm: the base permission associated with the extended permission
 * @xperm: xperm permissions
 * @ssid: AVC entry source sid
 * @tsid: AVC entry target sid
@@ -820,10 +827,9 @@ int __init avc_add_callback(int (*callback)(u32 event), u32 events)
 * 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, u8 driver, u8 xperm, u32 ssid,
			   u32 tsid, u16 tclass, u32 seqno,
			   struct extended_perms_decision *xpd,
			   u32 flags)
static int avc_update_node(u32 event, u32 perms, u8 driver, u8 base_perm,
			   u8 xperm, u32 ssid, u32 tsid, u16 tclass, u32 seqno,
			   struct extended_perms_decision *xpd, u32 flags)
{
	u32 hvalue;
	int rc = 0;
@@ -880,7 +886,7 @@ static int avc_update_node(u32 event, u32 perms, u8 driver, u8 xperm, u32 ssid,
	case AVC_CALLBACK_GRANT:
		node->ae.avd.allowed |= perms;
		if (node->ae.xp_node && (flags & AVC_EXTENDED_PERMS))
			avc_xperms_allow_perm(node->ae.xp_node, driver, xperm);
			avc_xperms_allow_perm(node->ae.xp_node, driver, base_perm, xperm);
		break;
	case AVC_CALLBACK_TRY_REVOKE:
	case AVC_CALLBACK_REVOKE:
@@ -987,10 +993,9 @@ static noinline void avc_compute_av(u32 ssid, u32 tsid, u16 tclass,
	avc_insert(ssid, tsid, tclass, avd, xp_node);
}

static noinline int avc_denied(u32 ssid, u32 tsid,
			       u16 tclass, u32 requested,
			       u8 driver, u8 xperm, unsigned int flags,
			       struct av_decision *avd)
static noinline int avc_denied(u32 ssid, u32 tsid, u16 tclass, u32 requested,
			       u8 driver, u8 base_perm, u8 xperm,
			       unsigned int flags, struct av_decision *avd)
{
	if (flags & AVC_STRICT)
		return -EACCES;
@@ -999,7 +1004,7 @@ static noinline int avc_denied(u32 ssid, u32 tsid,
	    !(avd->flags & AVD_FLAGS_PERMISSIVE))
		return -EACCES;

	avc_update_node(AVC_CALLBACK_GRANT, requested, driver,
	avc_update_node(AVC_CALLBACK_GRANT, requested, driver, base_perm,
			xperm, ssid, tsid, tclass, avd->seqno, NULL, flags);
	return 0;
}
@@ -1012,7 +1017,8 @@ static noinline int avc_denied(u32 ssid, u32 tsid,
 * driver field is used to specify which set contains the permission.
 */
int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested,
			   u8 driver, u8 xperm, struct common_audit_data *ad)
			   u8 driver, u8 base_perm, u8 xperm,
			   struct common_audit_data *ad)
{
	struct avc_node *node;
	struct av_decision avd;
@@ -1047,22 +1053,23 @@ int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested,
	local_xpd.auditallow = &auditallow;
	local_xpd.dontaudit = &dontaudit;

	xpd = avc_xperms_decision_lookup(driver, xp_node);
	xpd = avc_xperms_decision_lookup(driver, base_perm, xp_node);
	if (unlikely(!xpd)) {
		/*
		 * Compute the extended_perms_decision only if the driver
		 * is flagged
		 * is flagged and the base permission is known.
		 */
		if (!security_xperm_test(xp_node->xp.drivers.p, driver)) {
		if (!security_xperm_test(xp_node->xp.drivers.p, driver) ||
		    !(xp_node->xp.base_perms & base_perm)) {
			avd.allowed &= ~requested;
			goto decision;
		}
		rcu_read_unlock();
		security_compute_xperms_decision(ssid, tsid, tclass,
						 driver, &local_xpd);
		security_compute_xperms_decision(ssid, tsid, tclass, driver,
						 base_perm, &local_xpd);
		rcu_read_lock();
		avc_update_node(AVC_CALLBACK_ADD_XPERMS, requested,
				driver, xperm, ssid, tsid, tclass, avd.seqno,
		avc_update_node(AVC_CALLBACK_ADD_XPERMS, requested, driver,
				base_perm, xperm, ssid, tsid, tclass, avd.seqno,
				&local_xpd, 0);
	} else {
		avc_quick_copy_xperms_decision(xperm, &local_xpd, xpd);
@@ -1075,8 +1082,8 @@ int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested,
decision:
	denied = requested & ~(avd.allowed);
	if (unlikely(denied))
		rc = avc_denied(ssid, tsid, tclass, requested,
				driver, xperm, AVC_EXTENDED_PERMS, &avd);
		rc = avc_denied(ssid, tsid, tclass, requested, driver,
				base_perm, xperm, AVC_EXTENDED_PERMS, &avd);

	rcu_read_unlock();

@@ -1110,7 +1117,7 @@ static noinline int avc_perm_nonode(u32 ssid, u32 tsid, u16 tclass,
	avc_compute_av(ssid, tsid, tclass, avd, &xp_node);
	denied = requested & ~(avd->allowed);
	if (unlikely(denied))
		return avc_denied(ssid, tsid, tclass, requested, 0, 0,
		return avc_denied(ssid, tsid, tclass, requested, 0, 0, 0,
				  flags, avd);
	return 0;
}
@@ -1158,7 +1165,7 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
	rcu_read_unlock();

	if (unlikely(denied))
		return avc_denied(ssid, tsid, tclass, requested, 0, 0,
		return avc_denied(ssid, tsid, tclass, requested, 0, 0, 0,
				  flags, avd);
	return 0;
}
+3 −3
Original line number Diff line number Diff line
@@ -3688,8 +3688,8 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file,
		return 0;

	isec = inode_security(inode);
	rc = avc_has_extended_perms(ssid, isec->sid, isec->sclass,
				    requested, driver, xperm, &ad);
	rc = avc_has_extended_perms(ssid, isec->sid, isec->sclass, requested,
				    driver, AVC_EXT_IOCTL, xperm, &ad);
out:
	return rc;
}
@@ -5952,7 +5952,7 @@ static int nlmsg_sock_has_extended_perms(struct sock *sk, u32 perms, u16 nlmsg_t
	xperm = nlmsg_type & 0xff;

	return avc_has_extended_perms(current_sid(), sksec->sid, sksec->sclass,
			perms, driver, xperm, &ad);
				      perms, driver, AVC_EXT_NLMSG, xperm, &ad);
}

static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
+4 −1
Original line number Diff line number Diff line
@@ -136,8 +136,11 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid, u16 tclass, u32 requested,
int avc_has_perm(u32 ssid, u32 tsid, u16 tclass, u32 requested,
		 struct common_audit_data *auditdata);

#define AVC_EXT_IOCTL	(1 << 0) /* Cache entry for an ioctl extended permission */
#define AVC_EXT_NLMSG	(1 << 1) /* Cache entry for an nlmsg extended permission */
int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested,
			   u8 driver, u8 perm, struct common_audit_data *ad);
			   u8 driver, u8 base_perm, u8 perm,
			   struct common_audit_data *ad);

u32 avc_policy_seqno(void);

+3 −0
Original line number Diff line number Diff line
@@ -239,6 +239,7 @@ struct extended_perms_data {
struct extended_perms_decision {
	u8 used;
	u8 driver;
	u8 base_perm;
	struct extended_perms_data *allowed;
	struct extended_perms_data *auditallow;
	struct extended_perms_data *dontaudit;
@@ -246,6 +247,7 @@ struct extended_perms_decision {

struct extended_perms {
	u16 len; /* length associated decision chain */
	u8 base_perms; /* which base permissions are covered */
	struct extended_perms_data drivers; /* flag drivers that are used */
};

@@ -257,6 +259,7 @@ void security_compute_av(u32 ssid, u32 tsid, u16 tclass,
			 struct extended_perms *xperms);

void security_compute_xperms_decision(u32 ssid, u32 tsid, u16 tclass, u8 driver,
				      u8 base_perm,
				      struct extended_perms_decision *xpermd);

void security_compute_av_user(u32 ssid, u32 tsid, u16 tclass,
+21 −7
Original line number Diff line number Diff line
@@ -582,7 +582,7 @@ static void type_attribute_bounds_av(struct policydb *policydb,
}

/*
 * Flag which drivers have permissions.
 * Flag which drivers have permissions and which base permissions are covered.
 */
void services_compute_xperms_drivers(
		struct extended_perms *xperms,
@@ -592,12 +592,19 @@ void services_compute_xperms_drivers(

	switch (node->datum.u.xperms->specified) {
	case AVTAB_XPERMS_IOCTLDRIVER:
		xperms->base_perms |= AVC_EXT_IOCTL;
		/* if one or more driver has all permissions allowed */
		for (i = 0; i < ARRAY_SIZE(xperms->drivers.p); i++)
			xperms->drivers.p[i] |= node->datum.u.xperms->perms.p[i];
		break;
	case AVTAB_XPERMS_IOCTLFUNCTION:
		xperms->base_perms |= AVC_EXT_IOCTL;
		/* if allowing permissions within a driver */
		security_xperm_set(xperms->drivers.p,
					node->datum.u.xperms->driver);
		break;
	case AVTAB_XPERMS_NLMSG:
		xperms->base_perms |= AVC_EXT_NLMSG;
		/* if allowing permissions within a driver */
		security_xperm_set(xperms->drivers.p,
					node->datum.u.xperms->driver);
@@ -631,8 +638,7 @@ static void context_struct_compute_av(struct policydb *policydb,
	avd->auditallow = 0;
	avd->auditdeny = 0xffffffff;
	if (xperms) {
		memset(&xperms->drivers, 0, sizeof(xperms->drivers));
		xperms->len = 0;
		memset(xperms, 0, sizeof(*xperms));
	}

	if (unlikely(!tclass || tclass > policydb->p_classes.nprim)) {
@@ -969,15 +975,21 @@ void services_compute_xperms_decision(struct extended_perms_decision *xpermd,
{
	switch (node->datum.u.xperms->specified) {
	case AVTAB_XPERMS_IOCTLFUNCTION:
	case AVTAB_XPERMS_NLMSG:
		if (xpermd->driver != node->datum.u.xperms->driver)
		if (xpermd->base_perm != AVC_EXT_IOCTL ||
		    xpermd->driver != node->datum.u.xperms->driver)
			return;
		break;
	case AVTAB_XPERMS_IOCTLDRIVER:
		if (!security_xperm_test(node->datum.u.xperms->perms.p,
		if (xpermd->base_perm != AVC_EXT_IOCTL ||
		    !security_xperm_test(node->datum.u.xperms->perms.p,
					 xpermd->driver))
			return;
		break;
	case AVTAB_XPERMS_NLMSG:
		if (xpermd->base_perm != AVC_EXT_NLMSG ||
		    xpermd->driver != node->datum.u.xperms->driver)
			return;
		break;
	default:
		pr_warn_once(
			"SELinux: unknown extended permission (%u) will be ignored\n",
@@ -1010,6 +1022,7 @@ void security_compute_xperms_decision(u32 ssid,
				      u32 tsid,
				      u16 orig_tclass,
				      u8 driver,
				      u8 base_perm,
				      struct extended_perms_decision *xpermd)
{
	struct selinux_policy *policy;
@@ -1023,6 +1036,7 @@ void security_compute_xperms_decision(u32 ssid,
	struct ebitmap_node *snode, *tnode;
	unsigned int i, j;

	xpermd->base_perm = base_perm;
	xpermd->driver = driver;
	xpermd->used = 0;
	memset(xpermd->allowed->p, 0, sizeof(xpermd->allowed->p));