diff mbox series

[4/8] libsepol: implement new kernel binary format for avtab

Message ID 20230531114914.2237609-5-juraj@jurajmarcin.com (mailing list archive)
State Superseded
Delegated to: Petr Lautrbach
Headers show
Series checkpolicy, libsepol: add prefix/suffix matching to filename type transitions | expand

Commit Message

Juraj Marcin May 31, 2023, 11:49 a.m. UTC
Implement a new binary policy format that closely matches the new
internal representation introduced in the previous patch.

This patch bumps the maximum kernel policy version and implements
reading/writing functions such that kernel binary policy structure
matches internal representation.

These changes can cause the binary policy to grow in size due to
effectively undoing the benefits of the commit 8206b8cb ("libsepol:
implement POLICYDB_VERSION_COMP_FTRANS "), but this will be mitigated by
adding the prefix/suffix support as described in the previous patch.

Reviewed-by: Ondrej Mosnacek <omosnace@redhat.com>
Signed-off-by: Juraj Marcin <juraj@jurajmarcin.com>
---
 libsepol/include/sepol/policydb/hashtab.h  |  8 ++
 libsepol/include/sepol/policydb/policydb.h |  3 +-
 libsepol/src/avtab.c                       | 88 ++++++++++++++++++++--
 libsepol/src/policydb.c                    |  8 ++
 libsepol/src/write.c                       | 86 +++++++++++++++++----
 5 files changed, 172 insertions(+), 21 deletions(-)
diff mbox series

Patch

diff --git a/libsepol/include/sepol/policydb/hashtab.h b/libsepol/include/sepol/policydb/hashtab.h
index 3a6b953e..92fedf69 100644
--- a/libsepol/include/sepol/policydb/hashtab.h
+++ b/libsepol/include/sepol/policydb/hashtab.h
@@ -116,6 +116,14 @@  static inline int hashtab_is_empty(hashtab_t h)
 	return !h || !h->size;
 }
 
+/* Returns number of elements in the hashtab h or 0 is h is NULL */
+static inline uint32_t hashtab_nel(hashtab_t h)
+{
+	if (!h)
+		return 0;
+	return h->nel;
+}
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libsepol/include/sepol/policydb/policydb.h b/libsepol/include/sepol/policydb/policydb.h
index d30f26af..528c1cad 100644
--- a/libsepol/include/sepol/policydb/policydb.h
+++ b/libsepol/include/sepol/policydb/policydb.h
@@ -722,10 +722,11 @@  extern int policydb_set_target_platform(policydb_t *p, int platform);
 #define POLICYDB_VERSION_INFINIBAND		31 /* Linux-specific */
 #define POLICYDB_VERSION_GLBLUB		32
 #define POLICYDB_VERSION_COMP_FTRANS	33 /* compressed filename transitions */
+#define POLICYDB_VERSION_AVTAB_FTRANS	34 /* filename transitions moved to avtab */
 
 /* Range of policy versions we understand*/
 #define POLICYDB_VERSION_MIN	POLICYDB_VERSION_BASE
-#define POLICYDB_VERSION_MAX	POLICYDB_VERSION_COMP_FTRANS
+#define POLICYDB_VERSION_MAX	POLICYDB_VERSION_AVTAB_FTRANS
 
 /* Module versions and specific changes*/
 #define MOD_POLICYDB_VERSION_BASE		4
diff --git a/libsepol/src/avtab.c b/libsepol/src/avtab.c
index 3d8d6769..33441a34 100644
--- a/libsepol/src/avtab.c
+++ b/libsepol/src/avtab.c
@@ -446,6 +446,87 @@  void avtab_hash_eval(avtab_t * h, char *tag)
 	     tag, h->nel, slots_used, h->nslot, max_chain_len);
 }
 
+static int avtab_read_name_trans(policy_file_t *fp, symtab_t *target)
+{
+	int rc;
+	uint32_t buf32[2], nel, i, len, *otype = NULL;
+	char *name = NULL;
+
+	/* read number of name transitions */
+	rc = next_entry(buf32, fp, sizeof(uint32_t) * 1);
+	if (rc < 0)
+		return rc;
+	nel = le32_to_cpu(buf32[0]);
+
+	rc = symtab_init(target, nel);
+	if (rc < 0)
+		return rc;
+
+	/* read name transitions */
+	for (i = 0; i < nel; i++) {
+		rc = SEPOL_ENOMEM;
+		otype = malloc(sizeof(uint32_t));
+		if (!otype)
+			goto exit;
+
+		/* read name transition otype and name length */
+		rc = next_entry(buf32, fp, sizeof(uint32_t) * 2);
+		if (rc < 0)
+			goto exit;
+		*otype = le32_to_cpu(buf32[0]);
+		len = le32_to_cpu(buf32[1]);
+
+		/* read the name */
+		rc = str_read(&name, fp, len);
+		if (rc < 0)
+			goto exit;
+
+		rc = hashtab_insert(target->table, name, otype);
+		if (rc < 0)
+			goto exit;
+		otype = NULL;
+		name = NULL;
+	}
+
+exit:
+	free(otype);
+	free(name);
+	return rc;
+}
+
+static int avtab_trans_read(policy_file_t *fp, uint32_t vers,
+			    avtab_trans_t *trans)
+{
+	int rc;
+	uint32_t buf32[1];
+
+	if (vers < POLICYDB_VERSION_AVTAB_FTRANS) {
+		rc = next_entry(buf32, fp, sizeof(uint32_t));
+		if (rc < 0) {
+			ERR(fp->handle, "truncated entry");
+			return SEPOL_ERR;
+		}
+		trans->otype = le32_to_cpu(*buf32);
+		return SEPOL_OK;
+	}
+
+	/* read otype */
+	rc = next_entry(buf32, fp, sizeof(uint32_t) * 1);
+	if (rc < 0)
+		return rc;
+	trans->otype = le32_to_cpu(buf32[0]);
+
+	rc = avtab_read_name_trans(fp, &trans->name_trans);
+	if (rc < 0)
+		goto bad;
+
+	return SEPOL_OK;
+
+bad:
+	avtab_trans_destroy(trans);
+	return rc;
+}
+
 /* Ordering of datums in the original avtab format in the policy file. */
 static const uint16_t spec_order[] = {
 	AVTAB_ALLOWED,
@@ -609,12 +690,9 @@  int avtab_read_item(struct policy_file *fp, uint32_t vers, avtab_t * a,
 			xperms.perms[i] = le32_to_cpu(buf32[i]);
 		datum.xperms = &xperms;
 	} else if (key.specified & AVTAB_TRANSITION) {
-		rc = next_entry(buf32, fp, sizeof(uint32_t));
-		if (rc < 0) {
-			ERR(fp->handle, "truncated entry");
+		rc = avtab_trans_read(fp, vers, &trans);
+		if (rc < 0)
 			return -1;
-		}
-		trans.otype = le32_to_cpu(*buf32);
 		datum.trans = &trans;
 	} else {
 		rc = next_entry(buf32, fp, sizeof(uint32_t));
diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
index 30c70d29..f2271524 100644
--- a/libsepol/src/policydb.c
+++ b/libsepol/src/policydb.c
@@ -208,6 +208,13 @@  static const struct policydb_compat_info policydb_compat[] = {
 	 .ocon_num = OCON_IBENDPORT + 1,
 	 .target_platform = SEPOL_TARGET_SELINUX,
 	},
+	{
+	 .type = POLICY_KERN,
+	 .version = POLICYDB_VERSION_AVTAB_FTRANS,
+	 .sym_num = SYM_NUM,
+	 .ocon_num = OCON_IBENDPORT + 1,
+	 .target_platform = SEPOL_TARGET_SELINUX,
+	},
 	{
 	 .type = POLICY_BASE,
 	 .version = MOD_POLICYDB_VERSION_BASE,
@@ -4099,6 +4106,7 @@  int policydb_read(policydb_t * p, struct policy_file *fp, unsigned verbose)
 		if (role_allow_read(&p->role_allow, fp))
 			goto bad;
 		if (r_policyvers >= POLICYDB_VERSION_FILENAME_TRANS &&
+		    r_policyvers < POLICYDB_VERSION_AVTAB_FTRANS &&
 		    avtab_filename_trans_read(fp, r_policyvers, &p->te_avtab))
 			goto bad;
 	} else {
diff --git a/libsepol/src/write.c b/libsepol/src/write.c
index 7b0ef705..12f14670 100644
--- a/libsepol/src/write.c
+++ b/libsepol/src/write.c
@@ -102,6 +102,56 @@  static uint16_t spec_order[] = {
 	AVTAB_MEMBER
 };
 
+static int avtab_trans_write_helper(hashtab_key_t hkey, hashtab_datum_t hdatum,
+				    void *fp)
+{
+	char *name = hkey;
+	uint32_t *otype = hdatum;
+	uint32_t buf32[2], len;
+	size_t items;
+
+	/* write filename transition otype and name length */
+	len = strlen(name);
+	buf32[0] = cpu_to_le32(*otype);
+	buf32[1] = cpu_to_le32(len);
+	items = put_entry(buf32, sizeof(uint32_t), 2, fp);
+	if (items != 2)
+		return -1;
+
+	/* write filename transition name */
+	items = put_entry(name, sizeof(char), len, fp);
+	if (items != len)
+		return -1;
+
+	return 0;
+}
+
+static int avtab_trans_write(policydb_t *p, const avtab_trans_t *cur,
+			     policy_file_t *fp)
+{
+	size_t items;
+	uint32_t buf32[2];
+
+	if (p->policyvers >= POLICYDB_VERSION_AVTAB_FTRANS) {
+		/* write otype and number of filename transitions */
+		buf32[0] = cpu_to_le32(cur->otype);
+		buf32[1] = cpu_to_le32(hashtab_nel(cur->name_trans.table));
+		items = put_entry(buf32, sizeof(uint32_t), 2, fp);
+		if (items != 2)
+			return -1;
+
+		/* write filename transitions */
+		return hashtab_map(cur->name_trans.table,
+				   avtab_trans_write_helper, fp);
+	} else if (cur->otype) {
+		buf32[0] = cpu_to_le32(cur->otype);
+		items = put_entry(buf32, sizeof(uint32_t), 1, fp);
+		if (items != 1)
+			return -1;
+	}
+	return 0;
+}
+
 static int avtab_write_item(policydb_t * p,
 			    avtab_ptr_t cur, struct policy_file *fp,
 			    unsigned merge, unsigned commit, uint32_t * nel)
@@ -116,8 +166,12 @@  static int avtab_write_item(policydb_t * p,
 				&& p->policyvers < POLICYDB_VERSION_AVTAB);
 	unsigned int i;
 
-	/* skip entries which only contain filename transitions */
-	if (cur->key.specified & AVTAB_TRANSITION && !cur->datum.trans->otype) {
+	/*
+	 * skip entries which only contain filename transitions in versions
+	 * before filename transitions were moved to avtab
+	 */
+	if (p->policyvers < POLICYDB_VERSION_AVTAB_FTRANS &&
+	    cur->key.specified & AVTAB_TRANSITION && !cur->datum.trans->otype) {
 		/* if oldvers, reduce nel, because this node will be skipped */
 		if (oldvers && nel)
 			(*nel)--;
@@ -271,9 +325,7 @@  static int avtab_write_item(policydb_t * p,
 		if (items != 8)
 			return POLICYDB_ERROR;
 	} else if (cur->key.specified & AVTAB_TRANSITION) {
-		buf32[0] = cpu_to_le32(cur->datum.trans->otype);
-		items = put_entry(buf32, sizeof(uint32_t), 1, fp);
-		if (items != 1)
+		if (avtab_trans_write(p, cur->datum.trans, fp) < 0)
 			return POLICYDB_ERROR;
 	} else {
 		buf32[0] = cpu_to_le32(cur->datum.data);
@@ -326,15 +378,18 @@  static int avtab_write(struct policydb *p, avtab_t * a, struct policy_file *fp)
 		 * filename transitions.
 		 */
 		nel = a->nel;
-		/*
-		 * entries containing only filename transitions are skipped and
-		 * written out later
-		 */
-		for (i = 0; i < a->nslot; i++) {
-			for (cur = a->htable[i]; cur; cur = cur->next) {
-				if (cur->key.specified & AVTAB_TRANSITION &&
-				    !cur->datum.trans->otype)
-					nel--;
+		if (p->policyvers < POLICYDB_VERSION_AVTAB_FTRANS) {
+			/*
+			 * entries containing only filename transitions are
+			 * skipped and written out later
+			 */
+			for (i = 0; i < a->nslot; i++) {
+				for (cur = a->htable[i]; cur; cur = cur->next) {
+					if ((cur->key.specified
+					     & AVTAB_TRANSITION) &&
+					    !cur->datum.trans->otype)
+						nel--;
+				}
 			}
 		}
 		nel = cpu_to_le32(nel);
@@ -2625,7 +2680,8 @@  int policydb_write(policydb_t * p, struct policy_file *fp)
 		if (role_allow_write(p->role_allow, fp))
 			return POLICYDB_ERROR;
 		if (p->policyvers >= POLICYDB_VERSION_FILENAME_TRANS) {
-			if (avtab_filename_trans_write(p, &p->te_avtab, fp))
+			if (p->policyvers < POLICYDB_VERSION_AVTAB_FTRANS &&
+			    avtab_filename_trans_write(p, &p->te_avtab, fp))
 				return POLICYDB_ERROR;
 		} else if (avtab_has_filename_transitions(&p->te_avtab)) {
 			WARN(fp->handle,