1
0
Fork 0
mirror of synced 2025-03-06 20:59:54 +01:00

selinux/stable-6.13 PR 20250107

-----BEGIN PGP SIGNATURE-----
 
 iQJIBAABCAAyFiEES0KozwfymdVUl37v6iDy2pc3iXMFAmd9cIwUHHBhdWxAcGF1
 bC1tb29yZS5jb20ACgkQ6iDy2pc3iXOUCA//cioM7Z3f2NLzvV0O8zmQcnaEV1U3
 8RumFdUi+n2g7jUTgkOSJoPEsY7loeE/n/7tqD0D7r0nD5sgS4CsFtVdLfz/yVH5
 8tIslrJR18h3H70riL1fev7giNbdrftGJA1W0VSh2cwSqJUUpJ79skrZ9HgGOILW
 vDHoxv3IlzyNrc1XwfjaXXhr7xQMFRghjUGSBpIXk56iVeZebI/Bd1CcHiI1JPBl
 cea73AF8CiGvgjXh2IUPmxjVzJiM2Whjea0QT6RDg8dx2/NJ11jaYHNsKvjT14Z/
 H9s3wx4rl0+qwkmmp0zO3bK4It1X0I1XWrjvhjIxkAq7nZ0befNIJCfK7JXBT2TI
 vGP7kGDbIaN1183CcnXfZ3cpvQTzEanRn+CzLhXVuRgfFoIGIPMxYGRWYunNVjiN
 RW5ptv2pFmNRlCEthTAzwbzxk5ISlTspnsgyo5O5RhB2HGPbCyhEQN8K4rfJxzUy
 nSznOi8+TXdHpOwGFnU0Olhem2Yj0j0wo6c5nvB5+0NDVGFXULget2vHv00FTfOp
 uglGECdKf4Uc0+g6ZRrGjdxJtkEyZr6QAL43ccmQ+2WMb95R2nLq9YjOTpBMg1pD
 zUfcW9qensxAYucloCtHOdOAdd62udEVSERhna7ZPApUjVzaeD50l8SsasaWBxDL
 m6sl3gRDKWW9m2k=
 =ojzK
 -----END PGP SIGNATURE-----

Merge tag 'selinux-pr-20250107' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux

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
This commit is contained in:
Linus Torvalds 2025-01-07 14:49:48 -08:00
commit 09a0fa92e5
5 changed files with 65 additions and 38 deletions
security/selinux

View file

@ -174,13 +174,15 @@ int avc_get_hash_stats(char *page)
* using a linked list for extended_perms_decision lookup because the list is * using a linked list for extended_perms_decision lookup because the list is
* always small. i.e. less than 5, typically 1 * 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 *
struct avc_xperms_node *xp_node) avc_xperms_decision_lookup(u8 driver, u8 base_perm,
struct avc_xperms_node *xp_node)
{ {
struct avc_xperms_decision_node *xpd_node; struct avc_xperms_decision_node *xpd_node;
list_for_each_entry(xpd_node, &xp_node->xpd_head, xpd_list) { 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 &xpd_node->xpd;
} }
return NULL; 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, 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; struct extended_perms_decision *xpd;
security_xperm_set(xp_node->xp.drivers.p, driver); 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) if (xpd && xpd->allowed)
security_xperm_set(xpd->allowed->p, perm); 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, static void avc_copy_xperms_decision(struct extended_perms_decision *dest,
struct extended_perms_decision *src) struct extended_perms_decision *src)
{ {
dest->base_perm = src->base_perm;
dest->driver = src->driver; dest->driver = src->driver;
dest->used = src->used; dest->used = src->used;
if (dest->used & XPERMS_ALLOWED) if (dest->used & XPERMS_ALLOWED)
@ -272,6 +276,7 @@ static inline void avc_quick_copy_xperms_decision(u8 perm,
*/ */
u8 i = perm >> 5; u8 i = perm >> 5;
dest->base_perm = src->base_perm;
dest->used = src->used; dest->used = src->used;
if (dest->used & XPERMS_ALLOWED) if (dest->used & XPERMS_ALLOWED)
dest->allowed->p[i] = src->allowed->p[i]; 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)); memcpy(dest->xp.drivers.p, src->xp.drivers.p, sizeof(dest->xp.drivers.p));
dest->xp.len = src->xp.len; dest->xp.len = src->xp.len;
dest->xp.base_perms = src->xp.base_perms;
/* for each source xpd allocate a destination xpd and copy */ /* for each source xpd allocate a destination xpd and copy */
list_for_each_entry(src_xpd, &src->xpd_head, xpd_list) { list_for_each_entry(src_xpd, &src->xpd_head, xpd_list) {
@ -807,6 +813,7 @@ out:
* @event : Updating event * @event : Updating event
* @perms : Permission mask bits * @perms : Permission mask bits
* @driver: xperm driver information * @driver: xperm driver information
* @base_perm: the base permission associated with the extended permission
* @xperm: xperm permissions * @xperm: xperm permissions
* @ssid: AVC entry source sid * @ssid: AVC entry source sid
* @tsid: AVC entry target sid * @tsid: AVC entry target sid
@ -820,10 +827,9 @@ out:
* otherwise, this function updates the AVC entry. The original AVC-entry object * otherwise, this function updates the AVC entry. The original AVC-entry object
* will release later by RCU. * will release later by RCU.
*/ */
static int avc_update_node(u32 event, u32 perms, u8 driver, u8 xperm, u32 ssid, static int avc_update_node(u32 event, u32 perms, u8 driver, u8 base_perm,
u32 tsid, u16 tclass, u32 seqno, u8 xperm, u32 ssid, u32 tsid, u16 tclass, u32 seqno,
struct extended_perms_decision *xpd, struct extended_perms_decision *xpd, u32 flags)
u32 flags)
{ {
u32 hvalue; u32 hvalue;
int rc = 0; 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: case AVC_CALLBACK_GRANT:
node->ae.avd.allowed |= perms; node->ae.avd.allowed |= perms;
if (node->ae.xp_node && (flags & AVC_EXTENDED_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; break;
case AVC_CALLBACK_TRY_REVOKE: case AVC_CALLBACK_TRY_REVOKE:
case AVC_CALLBACK_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); avc_insert(ssid, tsid, tclass, avd, xp_node);
} }
static noinline int avc_denied(u32 ssid, u32 tsid, static noinline int avc_denied(u32 ssid, u32 tsid, u16 tclass, u32 requested,
u16 tclass, u32 requested, u8 driver, u8 base_perm, u8 xperm,
u8 driver, u8 xperm, unsigned int flags, unsigned int flags, struct av_decision *avd)
struct av_decision *avd)
{ {
if (flags & AVC_STRICT) if (flags & AVC_STRICT)
return -EACCES; return -EACCES;
@ -999,7 +1004,7 @@ static noinline int avc_denied(u32 ssid, u32 tsid,
!(avd->flags & AVD_FLAGS_PERMISSIVE)) !(avd->flags & AVD_FLAGS_PERMISSIVE))
return -EACCES; 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); xperm, ssid, tsid, tclass, avd->seqno, NULL, flags);
return 0; 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. * driver field is used to specify which set contains the permission.
*/ */
int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested, 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 avc_node *node;
struct av_decision avd; 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.auditallow = &auditallow;
local_xpd.dontaudit = &dontaudit; 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)) { if (unlikely(!xpd)) {
/* /*
* Compute the extended_perms_decision only if the driver * 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; avd.allowed &= ~requested;
goto decision; goto decision;
} }
rcu_read_unlock(); rcu_read_unlock();
security_compute_xperms_decision(ssid, tsid, tclass, security_compute_xperms_decision(ssid, tsid, tclass, driver,
driver, &local_xpd); base_perm, &local_xpd);
rcu_read_lock(); rcu_read_lock();
avc_update_node(AVC_CALLBACK_ADD_XPERMS, requested, avc_update_node(AVC_CALLBACK_ADD_XPERMS, requested, driver,
driver, xperm, ssid, tsid, tclass, avd.seqno, base_perm, xperm, ssid, tsid, tclass, avd.seqno,
&local_xpd, 0); &local_xpd, 0);
} else { } else {
avc_quick_copy_xperms_decision(xperm, &local_xpd, xpd); 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: decision:
denied = requested & ~(avd.allowed); denied = requested & ~(avd.allowed);
if (unlikely(denied)) if (unlikely(denied))
rc = avc_denied(ssid, tsid, tclass, requested, rc = avc_denied(ssid, tsid, tclass, requested, driver,
driver, xperm, AVC_EXTENDED_PERMS, &avd); base_perm, xperm, AVC_EXTENDED_PERMS, &avd);
rcu_read_unlock(); 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); avc_compute_av(ssid, tsid, tclass, avd, &xp_node);
denied = requested & ~(avd->allowed); denied = requested & ~(avd->allowed);
if (unlikely(denied)) if (unlikely(denied))
return avc_denied(ssid, tsid, tclass, requested, 0, 0, return avc_denied(ssid, tsid, tclass, requested, 0, 0, 0,
flags, avd); flags, avd);
return 0; return 0;
} }
@ -1158,7 +1165,7 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
rcu_read_unlock(); rcu_read_unlock();
if (unlikely(denied)) if (unlikely(denied))
return avc_denied(ssid, tsid, tclass, requested, 0, 0, return avc_denied(ssid, tsid, tclass, requested, 0, 0, 0,
flags, avd); flags, avd);
return 0; return 0;
} }

View file

@ -3688,8 +3688,8 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file,
return 0; return 0;
isec = inode_security(inode); isec = inode_security(inode);
rc = avc_has_extended_perms(ssid, isec->sid, isec->sclass, rc = avc_has_extended_perms(ssid, isec->sid, isec->sclass, requested,
requested, driver, xperm, &ad); driver, AVC_EXT_IOCTL, xperm, &ad);
out: out:
return rc; 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; xperm = nlmsg_type & 0xff;
return avc_has_extended_perms(current_sid(), sksec->sid, sksec->sclass, 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) static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)

View file

@ -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, int avc_has_perm(u32 ssid, u32 tsid, u16 tclass, u32 requested,
struct common_audit_data *auditdata); 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, 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); u32 avc_policy_seqno(void);

View file

@ -239,6 +239,7 @@ struct extended_perms_data {
struct extended_perms_decision { struct extended_perms_decision {
u8 used; u8 used;
u8 driver; u8 driver;
u8 base_perm;
struct extended_perms_data *allowed; struct extended_perms_data *allowed;
struct extended_perms_data *auditallow; struct extended_perms_data *auditallow;
struct extended_perms_data *dontaudit; struct extended_perms_data *dontaudit;
@ -246,6 +247,7 @@ struct extended_perms_decision {
struct extended_perms { struct extended_perms {
u16 len; /* length associated decision chain */ u16 len; /* length associated decision chain */
u8 base_perms; /* which base permissions are covered */
struct extended_perms_data drivers; /* flag drivers that are used */ 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); struct extended_perms *xperms);
void security_compute_xperms_decision(u32 ssid, u32 tsid, u16 tclass, u8 driver, void security_compute_xperms_decision(u32 ssid, u32 tsid, u16 tclass, u8 driver,
u8 base_perm,
struct extended_perms_decision *xpermd); struct extended_perms_decision *xpermd);
void security_compute_av_user(u32 ssid, u32 tsid, u16 tclass, void security_compute_av_user(u32 ssid, u32 tsid, u16 tclass,

View file

@ -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( void services_compute_xperms_drivers(
struct extended_perms *xperms, struct extended_perms *xperms,
@ -592,12 +592,19 @@ void services_compute_xperms_drivers(
switch (node->datum.u.xperms->specified) { switch (node->datum.u.xperms->specified) {
case AVTAB_XPERMS_IOCTLDRIVER: case AVTAB_XPERMS_IOCTLDRIVER:
xperms->base_perms |= AVC_EXT_IOCTL;
/* if one or more driver has all permissions allowed */ /* if one or more driver has all permissions allowed */
for (i = 0; i < ARRAY_SIZE(xperms->drivers.p); i++) for (i = 0; i < ARRAY_SIZE(xperms->drivers.p); i++)
xperms->drivers.p[i] |= node->datum.u.xperms->perms.p[i]; xperms->drivers.p[i] |= node->datum.u.xperms->perms.p[i];
break; break;
case AVTAB_XPERMS_IOCTLFUNCTION: 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: case AVTAB_XPERMS_NLMSG:
xperms->base_perms |= AVC_EXT_NLMSG;
/* if allowing permissions within a driver */ /* if allowing permissions within a driver */
security_xperm_set(xperms->drivers.p, security_xperm_set(xperms->drivers.p,
node->datum.u.xperms->driver); node->datum.u.xperms->driver);
@ -631,8 +638,7 @@ static void context_struct_compute_av(struct policydb *policydb,
avd->auditallow = 0; avd->auditallow = 0;
avd->auditdeny = 0xffffffff; avd->auditdeny = 0xffffffff;
if (xperms) { if (xperms) {
memset(&xperms->drivers, 0, sizeof(xperms->drivers)); memset(xperms, 0, sizeof(*xperms));
xperms->len = 0;
} }
if (unlikely(!tclass || tclass > policydb->p_classes.nprim)) { if (unlikely(!tclass || tclass > policydb->p_classes.nprim)) {
@ -969,13 +975,19 @@ void services_compute_xperms_decision(struct extended_perms_decision *xpermd,
{ {
switch (node->datum.u.xperms->specified) { switch (node->datum.u.xperms->specified) {
case AVTAB_XPERMS_IOCTLFUNCTION: case AVTAB_XPERMS_IOCTLFUNCTION:
case AVTAB_XPERMS_NLMSG: if (xpermd->base_perm != AVC_EXT_IOCTL ||
if (xpermd->driver != node->datum.u.xperms->driver) xpermd->driver != node->datum.u.xperms->driver)
return; return;
break; break;
case AVTAB_XPERMS_IOCTLDRIVER: case AVTAB_XPERMS_IOCTLDRIVER:
if (!security_xperm_test(node->datum.u.xperms->perms.p, if (xpermd->base_perm != AVC_EXT_IOCTL ||
xpermd->driver)) !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; return;
break; break;
default: default:
@ -1010,6 +1022,7 @@ void security_compute_xperms_decision(u32 ssid,
u32 tsid, u32 tsid,
u16 orig_tclass, u16 orig_tclass,
u8 driver, u8 driver,
u8 base_perm,
struct extended_perms_decision *xpermd) struct extended_perms_decision *xpermd)
{ {
struct selinux_policy *policy; struct selinux_policy *policy;
@ -1023,6 +1036,7 @@ void security_compute_xperms_decision(u32 ssid,
struct ebitmap_node *snode, *tnode; struct ebitmap_node *snode, *tnode;
unsigned int i, j; unsigned int i, j;
xpermd->base_perm = base_perm;
xpermd->driver = driver; xpermd->driver = driver;
xpermd->used = 0; xpermd->used = 0;
memset(xpermd->allowed->p, 0, sizeof(xpermd->allowed->p)); memset(xpermd->allowed->p, 0, sizeof(xpermd->allowed->p));