diff mbox series

[v2,6/8] checkpolicy,libsepol: add prefix/suffix support to kernel policy

Message ID af5e147c15852740ddd57897d2223702adeb0c33.1687251081.git.juraj@jurajmarcin.com (mailing list archive)
State Accepted
Commit 1174483d2924
Delegated to: Petr Lautrbach
Headers show
Series checkpolicy,libsepol: add prefix/suffix matching to filename type transitions | expand

Commit Message

Juraj Marcin June 20, 2023, 9:01 a.m. UTC
Currently, filename type transitions support only exact name matching.
However, in practice, the names contain variable parts. This leads to
many duplicated rules in the policy that differ only in the part of the
name, or it is even impossible to cover all possible combinations.

This patch extends the filename type transitions structures to include
new types of filename transitions - prefix and suffix filename
transitions. It also implements the reading and writing of those rules
in the kernel binary policy format together with increasing its version.

Reviewed-by: Ondrej Mosnacek <omosnace@redhat.com>
Signed-off-by: Juraj Marcin <juraj@jurajmarcin.com>
---
 checkpolicy/test/dispol.c                  |  25 ++++-
 libsepol/include/sepol/policydb/avtab.h    |   2 +
 libsepol/include/sepol/policydb/policydb.h |   9 +-
 libsepol/src/avtab.c                       |  13 +++
 libsepol/src/kernel_to_cil.c               |  30 +++++-
 libsepol/src/kernel_to_common.h            |   1 +
 libsepol/src/kernel_to_conf.c              |  30 +++++-
 libsepol/src/policydb.c                    |   7 ++
 libsepol/src/policydb_validate.c           |  11 +-
 libsepol/src/write.c                       | 113 +++++++++++++++++----
 10 files changed, 211 insertions(+), 30 deletions(-)
diff mbox series

Patch

diff --git a/checkpolicy/test/dispol.c b/checkpolicy/test/dispol.c
index e86ad2ed..efdd102b 100644
--- a/checkpolicy/test/dispol.c
+++ b/checkpolicy/test/dispol.c
@@ -129,6 +129,7 @@  typedef struct {
 	avtab_key_t *key;
 	policydb_t *p;
 	FILE *fp;
+	name_trans_match_t match;
 } render_name_trans_args_t;
 
 static int render_name_trans_helper(hashtab_key_t k, hashtab_datum_t d, void *a)
@@ -140,7 +141,22 @@  static int render_name_trans_helper(hashtab_key_t k, hashtab_datum_t d, void *a)
 	fprintf(args->fp, "type_transition ");
 	render_key(args->key, args->p, args->fp);
 	render_type(*otype, args->p, args->fp);
-	fprintf(args->fp, " \"%s\";\n", name);
+	const char *match_str = "";
+	switch (args->match) {
+	case NAME_TRANS_MATCH_EXACT:
+		match_str = "";
+		break;
+	case NAME_TRANS_MATCH_PREFIX:
+		match_str = " PREFIX";
+		break;
+	case NAME_TRANS_MATCH_SUFFIX:
+		match_str = " SUFFIX";
+		break;
+	default:
+		fprintf(args->fp, "     ERROR: no valid name match type specified\n");
+		return -1;
+	}
+	fprintf(args->fp, " \"%s\"%s;\n", name, match_str);
 
 	return 0;
 }
@@ -207,9 +223,16 @@  static int render_av_rule(avtab_key_t * key, avtab_datum_t * datum, uint32_t wha
 				.key = key,
 				.p = p,
 				.fp = fp,
+				.match = NAME_TRANS_MATCH_EXACT,
 			};
 			hashtab_map(datum->trans->name_trans.table,
 				    render_name_trans_helper, &args);
+			args.match = NAME_TRANS_MATCH_PREFIX;
+			hashtab_map(datum->trans->prefix_trans.table,
+				    render_name_trans_helper, &args);
+			args.match = NAME_TRANS_MATCH_SUFFIX;
+			hashtab_map(datum->trans->suffix_trans.table,
+				    render_name_trans_helper, &args);
 		}
 		if (key->specified & AVTAB_MEMBER) {
 			fprintf(fp, "type_member ");
diff --git a/libsepol/include/sepol/policydb/avtab.h b/libsepol/include/sepol/policydb/avtab.h
index 5dc720cc..870fb08a 100644
--- a/libsepol/include/sepol/policydb/avtab.h
+++ b/libsepol/include/sepol/policydb/avtab.h
@@ -74,6 +74,8 @@  typedef struct avtab_key {
 typedef struct avtab_trans {
 	uint32_t otype;		/* resulting type of the new object */
 	symtab_t name_trans;	/* filename transitions */
+	symtab_t prefix_trans;	/* prefix filename transitions */
+	symtab_t suffix_trans;	/* prefix filename transitions */
 } avtab_trans_t;
 
 typedef struct avtab_extended_perms {
diff --git a/libsepol/include/sepol/policydb/policydb.h b/libsepol/include/sepol/policydb/policydb.h
index 5efd0a47..a2df4a62 100644
--- a/libsepol/include/sepol/policydb/policydb.h
+++ b/libsepol/include/sepol/policydb/policydb.h
@@ -252,6 +252,12 @@  typedef struct av_extended_perms {
 	uint32_t perms[EXTENDED_PERMS_LEN];
 } av_extended_perms_t;
 
+typedef enum name_trans_match {
+	NAME_TRANS_MATCH_EXACT,
+	NAME_TRANS_MATCH_PREFIX,
+	NAME_TRANS_MATCH_SUFFIX,
+} name_trans_match_t;
+
 typedef struct avrule {
 /* these typedefs are almost exactly the same as those in avtab.h - they are
  * here because of the need to include neverallow and dontaudit messages */
@@ -723,10 +729,11 @@  extern int policydb_set_target_platform(policydb_t *p, int platform);
 #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 */
+#define POLICYDB_VERSION_PREFIX_SUFFIX	35 /* prefix/suffix support for filename transitions */
 
 /* Range of policy versions we understand*/
 #define POLICYDB_VERSION_MIN	POLICYDB_VERSION_BASE
-#define POLICYDB_VERSION_MAX	POLICYDB_VERSION_AVTAB_FTRANS
+#define POLICYDB_VERSION_MAX	POLICYDB_VERSION_PREFIX_SUFFIX
 
 /* Module versions and specific changes*/
 #define MOD_POLICYDB_VERSION_BASE		4
diff --git a/libsepol/src/avtab.c b/libsepol/src/avtab.c
index 2a9564ba..90cfb90b 100644
--- a/libsepol/src/avtab.c
+++ b/libsepol/src/avtab.c
@@ -327,6 +327,10 @@  void avtab_trans_destroy(avtab_trans_t *trans)
 {
 	hashtab_map(trans->name_trans.table, avtab_trans_destroy_helper, NULL);
 	symtab_destroy(&trans->name_trans);
+	hashtab_map(trans->prefix_trans.table, avtab_trans_destroy_helper, NULL);
+	symtab_destroy(&trans->prefix_trans);
+	hashtab_map(trans->suffix_trans.table, avtab_trans_destroy_helper, NULL);
+	symtab_destroy(&trans->suffix_trans);
 }
 
 void avtab_destroy(avtab_t * h)
@@ -520,6 +524,15 @@  static int avtab_trans_read(policy_file_t *fp, uint32_t vers,
 	if (rc < 0)
 		goto bad;
 
+	if (vers >= POLICYDB_VERSION_PREFIX_SUFFIX) {
+		rc = avtab_read_name_trans(fp, &trans->prefix_trans);
+		if (rc < 0)
+			goto bad;
+		rc = avtab_read_name_trans(fp, &trans->suffix_trans);
+		if (rc < 0)
+			goto bad;
+	}
+
 	return SEPOL_OK;
 
 bad:
diff --git a/libsepol/src/kernel_to_cil.c b/libsepol/src/kernel_to_cil.c
index e829e235..8824fe01 100644
--- a/libsepol/src/kernel_to_cil.c
+++ b/libsepol/src/kernel_to_cil.c
@@ -1707,9 +1707,24 @@  static int name_trans_to_strs_helper(hashtab_key_t k, hashtab_datum_t d, void *a
 	char *name = k;
 	uint32_t *otype = d;
 	name_trans_to_strs_args_t *args = a;
-	return strs_create_and_add(args->strs, "(%s %s %s %s \"%s\" %s)", 6,
+	const char *match_str = "";
+	switch (args->match) {
+	case NAME_TRANS_MATCH_EXACT:
+		match_str = "";
+		break;
+	case NAME_TRANS_MATCH_PREFIX:
+		match_str = " prefix";
+		break;
+	case NAME_TRANS_MATCH_SUFFIX:
+		match_str = " suffix";
+		break;
+	default:
+		ERR(NULL, "Unknown name match type: %" PRIu8, args->match);
+		return SEPOL_ERR;
+	}
+	return strs_create_and_add(args->strs, "(%s %s %s %s \"%s\"%s %s)", 7,
 				   args->flavor, args->src, args->tgt,
-				   args->class, name,
+				   args->class, name, match_str,
 				   args->pdb->p_type_val_to_name[*otype - 1]);
 }
 
@@ -1797,9 +1812,20 @@  static int avtab_node_to_strs(struct policydb *pdb, avtab_key_t *key, avtab_datu
 			.src = src,
 			.tgt = tgt,
 			.class = class,
+			.match = NAME_TRANS_MATCH_EXACT,
 		};
 		rc = hashtab_map(datum->trans->name_trans.table,
 				 name_trans_to_strs_helper, &args);
+		if (rc < 0)
+			return rc;
+		args.match = NAME_TRANS_MATCH_PREFIX;
+		rc = hashtab_map(datum->trans->prefix_trans.table,
+				 name_trans_to_strs_helper, &args);
+		if (rc < 0)
+			return rc;
+		args.match = NAME_TRANS_MATCH_SUFFIX;
+		rc = hashtab_map(datum->trans->suffix_trans.table,
+				 name_trans_to_strs_helper, &args);
 	} else {
 		new = pdb->p_type_val_to_name[data - 1];
 
diff --git a/libsepol/src/kernel_to_common.h b/libsepol/src/kernel_to_common.h
index 524a4b0f..96169228 100644
--- a/libsepol/src/kernel_to_common.h
+++ b/libsepol/src/kernel_to_common.h
@@ -90,6 +90,7 @@  typedef struct {
 	const char *src;
 	const char *tgt;
 	const char *class;
+	name_trans_match_t match;
 } name_trans_to_strs_args_t;
 
 void sepol_indent(FILE *out, int indent);
diff --git a/libsepol/src/kernel_to_conf.c b/libsepol/src/kernel_to_conf.c
index a637c207..5f232f47 100644
--- a/libsepol/src/kernel_to_conf.c
+++ b/libsepol/src/kernel_to_conf.c
@@ -1685,11 +1685,26 @@  static int name_trans_to_strs_helper(hashtab_key_t k, hashtab_datum_t d, void *a
 	char *name = k;
 	uint32_t *otype = d;
 	name_trans_to_strs_args_t *args = a;
-	return strs_create_and_add(args->strs, "%s %s %s:%s %s \"%s\";", 6,
+	const char *match_str = "";
+	switch (args->match) {
+	case NAME_TRANS_MATCH_EXACT:
+		match_str = "";
+		break;
+	case NAME_TRANS_MATCH_PREFIX:
+		match_str = " PREFIX";
+		break;
+	case NAME_TRANS_MATCH_SUFFIX:
+		match_str = " SUFFIX";
+		break;
+	default:
+		ERR(NULL, "Unknown name match type: %" PRIu8, args->match);
+		return SEPOL_ERR;
+	}
+	return strs_create_and_add(args->strs, "%s %s %s:%s %s \"%s\"%s;", 7,
 				   args->flavor, args->src, args->tgt,
 				   args->class,
 				   args->pdb->p_type_val_to_name[*otype - 1],
-				   name);
+				   name, match_str);
 }
 
 static int avtab_node_to_strs(struct policydb *pdb, avtab_key_t *key, avtab_datum_t *datum, struct strs *strs)
@@ -1773,9 +1788,20 @@  static int avtab_node_to_strs(struct policydb *pdb, avtab_key_t *key, avtab_datu
 			.src = src,
 			.tgt = tgt,
 			.class = class,
+			.match = NAME_TRANS_MATCH_EXACT,
 		};
 		rc = hashtab_map(datum->trans->name_trans.table,
 				 name_trans_to_strs_helper, &args);
+		if (rc < 0)
+			return rc;
+		args.match = NAME_TRANS_MATCH_PREFIX;
+		rc = hashtab_map(datum->trans->prefix_trans.table,
+				 name_trans_to_strs_helper, &args);
+		if (rc < 0)
+			return rc;
+		args.match = NAME_TRANS_MATCH_SUFFIX;
+		rc = hashtab_map(datum->trans->suffix_trans.table,
+				 name_trans_to_strs_helper, &args);
 	} else {
 		new = pdb->p_type_val_to_name[data - 1];
 
diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
index 37bb97a1..f1f6cec6 100644
--- a/libsepol/src/policydb.c
+++ b/libsepol/src/policydb.c
@@ -215,6 +215,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_PREFIX_SUFFIX,
+	 .sym_num = SYM_NUM,
+	 .ocon_num = OCON_IBENDPORT + 1,
+	 .target_platform = SEPOL_TARGET_SELINUX,
+	},
 	{
 	 .type = POLICY_BASE,
 	 .version = MOD_POLICYDB_VERSION_BASE,
diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 0b8e8eee..08b4a477 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -855,11 +855,18 @@  static int validate_avtab_key_and_datum(avtab_key_t *k, avtab_datum_t *d, void *
 
 		/* also each transition must be non empty */
 		if (!d->trans->otype &&
-		    !hashtab_nel(d->trans->name_trans.table))
+		    !hashtab_nel(d->trans->name_trans.table) &&
+		    !hashtab_nel(d->trans->name_trans.table) &&
+		    !hashtab_nel(d->trans->prefix_trans.table) &&
+		    !hashtab_nel(d->trans->suffix_trans.table))
 			return -1;
 
-		/* and each filename transition must be also valid */
+		/* and each name transition must be also valid */
 		if (hashtab_map(d->trans->name_trans.table,
+				validate_name_trans_helper, margs) ||
+		    hashtab_map(d->trans->prefix_trans.table,
+				validate_name_trans_helper, margs) ||
+		    hashtab_map(d->trans->suffix_trans.table,
 				validate_name_trans_helper, margs))
 			return -1;
 	} else if ((k->specified & AVTAB_TYPE) && validate_simpletype(d->data, margs->policy, margs->flavors)) {
diff --git a/libsepol/src/write.c b/libsepol/src/write.c
index d7f47c8d..df47197c 100644
--- a/libsepol/src/write.c
+++ b/libsepol/src/write.c
@@ -133,16 +133,43 @@  static int avtab_trans_write(policydb_t *p, const avtab_trans_t *cur,
 	uint32_t buf32[2];
 
 	if (p->policyvers >= POLICYDB_VERSION_AVTAB_FTRANS) {
-		/* write otype and number of filename transitions */
+		/* write otype and number of name 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);
+		/* write name transitions */
+		if (hashtab_map(cur->name_trans.table,
+				avtab_trans_write_helper, fp))
+			return -1;
+
+		if (p->policyvers >= POLICYDB_VERSION_PREFIX_SUFFIX) {
+			/* write number of prefix transitions */
+			buf32[0] = cpu_to_le32(hashtab_nel(
+					cur->prefix_trans.table));
+			items = put_entry(buf32, sizeof(uint32_t), 1, fp);
+			if (items != 1)
+				return -1;
+
+			/* write prefix transitions */
+			if (hashtab_map(cur->prefix_trans.table,
+					avtab_trans_write_helper, fp))
+				return -1;
+
+			/* write number of suffix transitions */
+			buf32[0] = cpu_to_le32(hashtab_nel(
+					cur->suffix_trans.table));
+			items = put_entry(buf32, sizeof(uint32_t), 1, fp);
+			if (items != 1)
+				return -1;
+
+			/* write suffix transitions */
+			if (hashtab_map(cur->suffix_trans.table,
+					avtab_trans_write_helper, fp))
+				return -1;
+		}
 	} else if (cur->otype) {
 		buf32[0] = cpu_to_le32(cur->otype);
 		items = put_entry(buf32, sizeof(uint32_t), 1, fp);
@@ -168,14 +195,26 @@  static int avtab_write_item(policydb_t * p,
 
 	/*
 	 * skip entries which only contain filename transitions in versions
-	 * before filename transitions were moved to avtab
+	 * before filename transitions were moved to avtab,
+	 * skip entries which only contain prefix/suffix transitions in versions
+	 * before prefix/suffix filename transitions
 	 */
-	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)--;
-		return 0;
+	if (cur->key.specified & AVTAB_TRANSITION) {
+		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)--;
+			return 0;
+		}
+		if (p->policyvers < POLICYDB_VERSION_PREFIX_SUFFIX &&
+		    !cur->datum.trans->otype &&
+		    !hashtab_nel(cur->datum.trans->name_trans.table))
+			return 0;
 	}
 
 	if (oldvers) {
@@ -378,17 +417,27 @@  static int avtab_write(struct policydb *p, avtab_t * a, struct policy_file *fp)
 		 * filename transitions.
 		 */
 		nel = a->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--;
+		for (i = 0; i < a->nslot; i++) {
+			for (cur = a->htable[i]; cur; cur = cur->next) {
+				if (!(cur->key.specified & AVTAB_TRANSITION))
+					continue;
+                                if (p->policyvers < POLICYDB_VERSION_AVTAB_FTRANS &&
+				    !cur->datum.trans->otype) {
+                                        /*
+                                         * entries containing only filename
+                                         * transitions are skipped and written
+                                         * out later
+                                         */
+                                        nel--;
+				} else if (p->policyvers < POLICYDB_VERSION_PREFIX_SUFFIX &&
+					   !cur->datum.trans->otype &&
+					   !hashtab_nel(cur->datum.trans->name_trans.table)) {
+					/*
+					 * entries containing only prefix/suffix
+					 * transitions are not supported in
+					 * previous versions
+					 */
+					nel--;
 				}
 			}
 		}
@@ -2520,6 +2569,22 @@  static int avtab_has_filename_transitions(avtab_t *a)
 	return 0;
 }
 
+static int avtab_has_prefix_suffix_filename_transitions(avtab_t *a)
+{
+	uint32_t i;
+	struct avtab_node *cur;
+	for (i = 0; i < a->nslot; i++) {
+		for (cur = a->htable[i]; cur; cur = cur->next) {
+			if (cur->key.specified & AVTAB_TRANSITION) {
+				if (hashtab_nel(cur->datum.trans->prefix_trans.table)
+				    || hashtab_nel(cur->datum.trans->suffix_trans.table))
+					return 1;
+			}
+		}
+	}
+	return 0;
+}
+
 /*
  * Write the configuration data in a policy database
  * structure to a policy database binary representation
@@ -2686,6 +2751,10 @@  int policydb_write(policydb_t * p, struct policy_file *fp)
 	if (p->policy_type == POLICY_KERN) {
 		if (avtab_write(p, &p->te_avtab, fp))
 			return POLICYDB_ERROR;
+		if (avtab_has_prefix_suffix_filename_transitions(&p->te_avtab)) {
+			WARN(fp->handle,
+			     "Discarding filename prefix/suffix type transition rules");
+		}
 		if (p->policyvers < POLICYDB_VERSION_BOOL) {
 			if (p->p_bools.nprim)
 				WARN(fp->handle, "Discarding "