diff mbox series

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

Message ID 20230531114914.2237609-7-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
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           | 10 +++-
 libsepol/src/write.c                       | 55 ++++++++++++++++++++--
 10 files changed, 170 insertions(+), 12 deletions(-)
diff mbox series

Patch

diff --git a/checkpolicy/test/dispol.c b/checkpolicy/test/dispol.c
index 4455407c..064cbd4e 100644
--- a/checkpolicy/test/dispol.c
+++ b/checkpolicy/test/dispol.c
@@ -82,6 +82,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)
@@ -93,7 +94,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 = " MATCH_PREFIX";
+		break;
+	case NAME_TRANS_MATCH_SUFFIX:
+		match_str = " MATCH_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;
 }
@@ -160,9 +176,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 33441a34..45cbb3a1 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 655581ef..fd036fa9 100644
--- a/libsepol/src/kernel_to_cil.c
+++ b/libsepol/src/kernel_to_cil.c
@@ -1706,9 +1706,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 = " MATCH_PREFIX";
+		break;
+	case NAME_TRANS_MATCH_SUFFIX:
+		match_str = " MATCH_SUFFIX";
+		break;
+	default:
+		sepol_log_err("Unknown name match type: %" PRIu8, args->match);
+		return SEPOL_ERR;
+	}
+	return strs_create_and_add(args->strs, "(%s %s %s %s \"%s\"%s %s)", 6,
 				   args->flavor, args->src, args->tgt,
-				   args->class, name,
+				   args->class, name, match_str,
 				   args->pdb->p_type_val_to_name[*otype - 1]);
 }
 
@@ -1796,9 +1811,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 eee2e1a0..2a1cae85 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;
 
 __attribute__ ((format(printf, 1, 2)))
diff --git a/libsepol/src/kernel_to_conf.c b/libsepol/src/kernel_to_conf.c
index 065f84eb..50fad1fb 100644
--- a/libsepol/src/kernel_to_conf.c
+++ b/libsepol/src/kernel_to_conf.c
@@ -1684,11 +1684,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 = " MATCH_PREFIX";
+		break;
+	case NAME_TRANS_MATCH_SUFFIX:
+		match_str = " MATCH_SUFFIX";
+		break;
+	default:
+		sepol_log_err("Unknown name match type: %" PRIu8, args->match);
+		return SEPOL_ERR;
+	}
+	return strs_create_and_add(args->strs, "%s %s %s:%s %s \"%s\"%s;", 6,
 				   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)
@@ -1772,9 +1787,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 3776292a..bd1a151d 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 66e8cb54..74f56842 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -851,11 +851,17 @@  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 &&
-		    (!d->trans->name_trans.table || !d->trans->name_trans.table->nel))
+		    hashtab_is_empty(d->trans->name_trans.table) &&
+		    hashtab_is_empty(d->trans->prefix_trans.table) &&
+		    hashtab_is_empty(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 5ba3f2b0..92a7b6cf 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);
@@ -2519,6 +2546,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
@@ -2685,6 +2728,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 "