diff mbox series

[1/5] selinux: move transition to separate structure in avtab_datum

Message ID 20230531112927.1957093-2-juraj@jurajmarcin.com (mailing list archive)
State Changes Requested
Delegated to: Paul Moore
Headers show
Series selinux: add prefix/suffix matching to filename type transitions | expand

Commit Message

Juraj Marcin May 31, 2023, 11:29 a.m. UTC
This is a preparation to move filename transitions to be part of the
avtab data structure. To do that, we first need to create space for it
in the avtab_datum structure which holds the rule for certain
combination of stype, ttype and tclass.

As only type transitions have a special variant that uses a filename, it
would be suboptimal to add a (mostly empty) pointer to some structure to
all avtab rules.

Therefore, this patch adds a new structure to the union in avtab_datum
and moves the otype of the transition to this structure. In the next
patch, this structure will also hold filename transitions for the
combination of stype, ttype and tclass. As the union already contains a
pointer, the size of avtab_datum does not increase. The only trade-off
is that each transition requires at least 4 more bytes and structure
allocation.

Reviewed-by: Ondrej Mosnacek <omosnace@redhat.com>
Signed-off-by: Juraj Marcin <juraj@jurajmarcin.com>
---
 security/selinux/ss/avtab.c    | 60 ++++++++++++++++++++++++++++++----
 security/selinux/ss/avtab.h    |  7 +++-
 security/selinux/ss/services.c |  5 ++-
 3 files changed, 63 insertions(+), 9 deletions(-)
diff mbox series

Patch

diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c
index 6766edc0fe68..a7f348e4509d 100644
--- a/security/selinux/ss/avtab.c
+++ b/security/selinux/ss/avtab.c
@@ -24,6 +24,7 @@ 
 #include "policydb.h"
 
 static struct kmem_cache *avtab_node_cachep __ro_after_init;
+static struct kmem_cache *avtab_trans_cachep __ro_after_init;
 static struct kmem_cache *avtab_xperms_cachep __ro_after_init;
 
 /* Based on MurmurHash3, written by Austin Appleby and placed in the
@@ -71,6 +72,7 @@  avtab_insert_node(struct avtab *h, int hvalue,
 		  const struct avtab_key *key, const struct avtab_datum *datum)
 {
 	struct avtab_node *newnode;
+	struct avtab_trans *trans;
 	struct avtab_extended_perms *xperms;
 	newnode = kmem_cache_zalloc(avtab_node_cachep, GFP_KERNEL);
 	if (newnode == NULL)
@@ -85,6 +87,14 @@  avtab_insert_node(struct avtab *h, int hvalue,
 		}
 		*xperms = *(datum->u.xperms);
 		newnode->datum.u.xperms = xperms;
+	} else if (key->specified & AVTAB_TRANSITION) {
+		trans = kmem_cache_zalloc(avtab_trans_cachep, GFP_KERNEL);
+		if (!trans) {
+			kmem_cache_free(avtab_node_cachep, newnode);
+			return NULL;
+		}
+		*trans = *datum->u.trans;
+		newnode->datum.u.trans = trans;
 	} else {
 		newnode->datum.u.data = datum->u.data;
 	}
@@ -289,9 +299,13 @@  void avtab_destroy(struct avtab *h)
 		while (cur) {
 			temp = cur;
 			cur = cur->next;
-			if (temp->key.specified & AVTAB_XPERMS)
+			if (temp->key.specified & AVTAB_XPERMS) {
 				kmem_cache_free(avtab_xperms_cachep,
 						temp->datum.u.xperms);
+			} else if (temp->key.specified & AVTAB_TRANSITION) {
+				kmem_cache_free(avtab_trans_cachep,
+						temp->datum.u.trans);
+			}
 			kmem_cache_free(avtab_node_cachep, temp);
 		}
 	}
@@ -407,6 +421,7 @@  int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
 	u32 items, items2, val, vers = pol->policyvers;
 	struct avtab_key key;
 	struct avtab_datum datum;
+	struct avtab_trans trans;
 	struct avtab_extended_perms xperms;
 	__le32 buf32[ARRAY_SIZE(xperms.perms.p)];
 	int i, rc;
@@ -473,7 +488,16 @@  int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
 		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++]);
+				if (key.specified & AVTAB_TRANSITION) {
+					memset(&trans, 0,
+					       sizeof(struct avtab_trans));
+					trans.otype =
+						le32_to_cpu(buf32[items++]);
+					datum.u.trans = &trans;
+				} else {
+					datum.u.data =
+						le32_to_cpu(buf32[items++]);
+				}
 				rc = insertf(a, &key, &datum, p);
 				if (rc)
 					return rc;
@@ -543,6 +567,15 @@  int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
 		for (i = 0; i < ARRAY_SIZE(xperms.perms.p); i++)
 			xperms.perms.p[i] = le32_to_cpu(buf32[i]);
 		datum.u.xperms = &xperms;
+	} else if (key.specified & AVTAB_TRANSITION) {
+		memset(&trans, 0, sizeof(struct avtab_trans));
+		rc = next_entry(buf32, fp, sizeof(u32));
+		if (rc) {
+			pr_err("SELinux: avtab: truncated entry\n");
+			return rc;
+		}
+		trans.otype = le32_to_cpu(*buf32);
+		datum.u.trans = &trans;
 	} else {
 		rc = next_entry(buf32, fp, sizeof(u32));
 		if (rc) {
@@ -551,12 +584,19 @@  int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
 		}
 		datum.u.data = le32_to_cpu(*buf32);
 	}
-	if ((key.specified & AVTAB_TYPE) &&
-	    !policydb_type_isvalid(pol, datum.u.data)) {
-		pr_err("SELinux: avtab: invalid type\n");
-		return -EINVAL;
+	if (key.specified & AVTAB_TRANSITION) {
+		if (!policydb_type_isvalid(pol, datum.u.trans->otype)) {
+			pr_err("SELinux: avtab: invalid transition type\n");
+			return -EINVAL;
+		}
+	} else if (key.specified & AVTAB_TYPE) {
+		if (!policydb_type_isvalid(pol, datum.u.data)) {
+			pr_err("SELinux: avtab: invalid type\n");
+			return -EINVAL;
+		}
 	}
-	return insertf(a, &key, &datum, p);
+	rc = insertf(a, &key, &datum, p);
+	return rc;
 }
 
 static int avtab_insertf(struct avtab *a, const struct avtab_key *k,
@@ -635,6 +675,9 @@  int avtab_write_item(struct policydb *p, const struct avtab_node *cur, void *fp)
 			buf32[i] = cpu_to_le32(cur->datum.u.xperms->perms.p[i]);
 		rc = put_entry(buf32, sizeof(u32),
 				ARRAY_SIZE(cur->datum.u.xperms->perms.p), fp);
+	} else if (cur->key.specified & AVTAB_TRANSITION) {
+		buf32[0] = cpu_to_le32(cur->datum.u.trans->otype);
+		rc = put_entry(buf32, sizeof(u32), 1, fp);
 	} else {
 		buf32[0] = cpu_to_le32(cur->datum.u.data);
 		rc = put_entry(buf32, sizeof(u32), 1, fp);
@@ -673,6 +716,9 @@  void __init avtab_cache_init(void)
 	avtab_node_cachep = kmem_cache_create("avtab_node",
 					      sizeof(struct avtab_node),
 					      0, SLAB_PANIC, NULL);
+	avtab_trans_cachep = kmem_cache_create("avtab_trans",
+					       sizeof(struct avtab_trans),
+					       0, SLAB_PANIC, NULL);
 	avtab_xperms_cachep = kmem_cache_create("avtab_extended_perms",
 						sizeof(struct avtab_extended_perms),
 						0, SLAB_PANIC, NULL);
diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h
index d6742fd9c560..6c8eb7c379cf 100644
--- a/security/selinux/ss/avtab.h
+++ b/security/selinux/ss/avtab.h
@@ -47,6 +47,10 @@  struct avtab_key {
 	u16 specified;	/* what field is specified */
 };
 
+struct avtab_trans {
+	u32 otype;		/* default resulting type of the new object */
+};
+
 /*
  * For operations that require more than the 32 permissions provided by the avc
  * extended permissions may be used to provide 256 bits of permissions.
@@ -69,7 +73,8 @@  struct avtab_extended_perms {
 
 struct avtab_datum {
 	union {
-		u32 data; /* access vector or type value */
+		u32 data; /* access vector, member or change value */
+		struct avtab_trans *trans;	/* transition value */
 		struct avtab_extended_perms *xperms;
 	} u;
 };
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 78946b71c1c1..8ed12406acba 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -1830,7 +1830,10 @@  static int security_compute_sid(u32 ssid,
 
 	if (avdatum) {
 		/* Use the type from the type transition/member/change rule. */
-		newcontext.type = avdatum->u.data;
+		if (avkey.specified & AVTAB_TRANSITION)
+			newcontext.type = avdatum->u.trans->otype;
+		else
+			newcontext.type = avdatum->u.data;
 	}
 
 	/* if we have a objname this is a file trans check so check those rules */