diff --git a/libsepol/cil/src/cil.c b/libsepol/cil/src/cil.c
index 38edcf8e..3b086de9 100644
--- a/libsepol/cil/src/cil.c
+++ b/libsepol/cil/src/cil.c
@@ -95,6 +95,9 @@ char *CIL_KEY_TUNABLEIF;
 char *CIL_KEY_ALLOW;
 char *CIL_KEY_DONTAUDIT;
 char *CIL_KEY_TYPETRANSITION;
+char *CIL_KEY_MATCH_EXACT;
+char *CIL_KEY_MATCH_PREFIX;
+char *CIL_KEY_MATCH_SUFFIX;
 char *CIL_KEY_TYPECHANGE;
 char *CIL_KEY_CALL;
 char *CIL_KEY_TUNABLE;
@@ -264,6 +267,9 @@ static void cil_init_keys(void)
 	CIL_KEY_ALLOW = cil_strpool_add("allow");
 	CIL_KEY_DONTAUDIT = cil_strpool_add("dontaudit");
 	CIL_KEY_TYPETRANSITION = cil_strpool_add("typetransition");
+	CIL_KEY_MATCH_EXACT = cil_strpool_add("match_exact");
+	CIL_KEY_MATCH_PREFIX = cil_strpool_add("match_prefix");
+	CIL_KEY_MATCH_SUFFIX = cil_strpool_add("match_suffix");
 	CIL_KEY_TYPECHANGE = cil_strpool_add("typechange");
 	CIL_KEY_CALL = cil_strpool_add("call");
 	CIL_KEY_TUNABLE = cil_strpool_add("tunable");
@@ -2387,6 +2393,8 @@ void cil_nametypetransition_init(struct cil_nametypetransition **nametypetrans)
 	(*nametypetrans)->obj = NULL;
 	(*nametypetrans)->name_str = NULL;
 	(*nametypetrans)->name = NULL;
+	(*nametypetrans)->name_match_str = NULL;
+	(*nametypetrans)->name_match = NAME_TRANS_MATCH_EXACT;
 	(*nametypetrans)->result_str = NULL;
 	(*nametypetrans)->result = NULL;
 }
diff --git a/libsepol/cil/src/cil_binary.c b/libsepol/cil/src/cil_binary.c
index ffa44be7..ea0cef32 100644
--- a/libsepol/cil/src/cil_binary.c
+++ b/libsepol/cil/src/cil_binary.c
@@ -1193,7 +1193,7 @@ static int __cil_typetransition_to_avtab_helper(policydb_t *pdb,
 						type_datum_t *sepol_src,
 						type_datum_t *sepol_tgt,
 						struct cil_list *class_list,
-						char *name,
+						char *name, uint8_t name_match,
 						type_datum_t *sepol_result)
 {
 	int rc;
@@ -1211,7 +1211,7 @@ static int __cil_typetransition_to_avtab_helper(policydb_t *pdb,
 		avt_key.target_type = sepol_tgt->s.value;
 		avt_key.target_class = sepol_obj->s.value;
 		rc = avtab_insert_filename_trans(&pdb->te_avtab, &avt_key,
-			sepol_result->s.value, name, NAME_TRANS_MATCH_EXACT,
+			sepol_result->s.value, name, name_match,
 			&otype);
 		if (rc != SEPOL_OK) {
 			if (rc == SEPOL_EEXIST) {
@@ -1280,7 +1280,7 @@ static int __cil_typetransition_to_avtab(policydb_t *pdb, const struct cil_db *d
 
 			rc = __cil_typetransition_to_avtab_helper(
 				pdb, sepol_src, sepol_src, class_list,
-				name, sepol_result
+				name, typetrans->name_match, sepol_result
 			);
 			if (rc != SEPOL_OK) goto exit;
 		}
@@ -1298,7 +1298,7 @@ static int __cil_typetransition_to_avtab(policydb_t *pdb, const struct cil_db *d
 
 				rc = __cil_typetransition_to_avtab_helper(
 					pdb, sepol_src, sepol_tgt, class_list,
-					name, sepol_result
+					name, typetrans->name_match, sepol_result
 				);
 				if (rc != SEPOL_OK) goto exit;
 			}
diff --git a/libsepol/cil/src/cil_build_ast.c b/libsepol/cil/src/cil_build_ast.c
index 4177c9f6..47513f92 100644
--- a/libsepol/cil/src/cil_build_ast.c
+++ b/libsepol/cil/src/cil_build_ast.c
@@ -3334,10 +3334,11 @@ int cil_gen_typetransition(struct cil_db *db, struct cil_tree_node *parse_curren
 		CIL_SYN_STRING,
 		CIL_SYN_STRING,
 		CIL_SYN_STRING | CIL_SYN_END,
-		CIL_SYN_END
+		CIL_SYN_STRING | CIL_SYN_END,
+		CIL_SYN_END,
 	};
 	size_t syntax_len = sizeof(syntax)/sizeof(*syntax);
-	char *s1, *s2, *s3, *s4, *s5;
+	char *s1, *s2, *s3, *s4, *s5, *s6;
 
 	if (db == NULL || parse_current == NULL || ast_node == NULL ) {
 		goto exit;
@@ -3353,16 +3354,27 @@ int cil_gen_typetransition(struct cil_db *db, struct cil_tree_node *parse_curren
 	s3 = parse_current->next->next->next->data;
 	s4 = parse_current->next->next->next->next->data;
 	s5 = NULL;
+	s6 = NULL;
 
 	if (parse_current->next->next->next->next->next) {
 		if (s4 == CIL_KEY_STAR) {
-			s4 = parse_current->next->next->next->next->next->data;
+			if (parse_current->next->next->next->next->next->next) {
+				s4 = parse_current->next->next->next->next->next->next->data;
+			} else {
+				s4 = parse_current->next->next->next->next->next->data;
+			}
 		} else {
-			s5 = parse_current->next->next->next->next->next->data;
+			if (parse_current->next->next->next->next->next->next) {
+				s5 = parse_current->next->next->next->next->next->data;
+				s6 = parse_current->next->next->next->next->next->next->data;
+			} else {
+				s5 = CIL_KEY_MATCH_EXACT;
+				s6 = parse_current->next->next->next->next->next->data;
+			}
 		}
 	}
 
-	if (s5) {
+	if (s6) {
 		struct cil_nametypetransition *nametypetrans = NULL;
 
 		cil_nametypetransition_init(&nametypetrans);
@@ -3370,8 +3382,9 @@ int cil_gen_typetransition(struct cil_db *db, struct cil_tree_node *parse_curren
 		nametypetrans->src_str = s1;
 		nametypetrans->tgt_str = s2;
 		nametypetrans->obj_str = s3;
-		nametypetrans->result_str = s5;
+		nametypetrans->result_str = s6;
 		nametypetrans->name_str = s4;
+		nametypetrans->name_match_str = s5;
 
 		ast_node->data = nametypetrans;
 		ast_node->flavor = CIL_NAMETYPETRANSITION;
diff --git a/libsepol/cil/src/cil_copy_ast.c b/libsepol/cil/src/cil_copy_ast.c
index 17f05021..a2d2fe40 100644
--- a/libsepol/cil/src/cil_copy_ast.c
+++ b/libsepol/cil/src/cil_copy_ast.c
@@ -726,6 +726,7 @@ int cil_copy_nametypetransition(__attribute__((unused)) struct cil_db *db, void
 	new->tgt_str = orig->tgt_str;
 	new->obj_str = orig->obj_str;
 	new->name_str = orig->name_str;
+	new->name_match_str = orig->name_match_str;
 	new->result_str = orig->result_str;
 
 
diff --git a/libsepol/cil/src/cil_internal.h b/libsepol/cil/src/cil_internal.h
index a7604762..f7a8d0f7 100644
--- a/libsepol/cil/src/cil_internal.h
+++ b/libsepol/cil/src/cil_internal.h
@@ -112,6 +112,9 @@ extern char *CIL_KEY_TUNABLEIF;
 extern char *CIL_KEY_ALLOW;
 extern char *CIL_KEY_DONTAUDIT;
 extern char *CIL_KEY_TYPETRANSITION;
+extern char *CIL_KEY_MATCH_EXACT;
+extern char *CIL_KEY_MATCH_PREFIX;
+extern char *CIL_KEY_MATCH_SUFFIX;
 extern char *CIL_KEY_TYPECHANGE;
 extern char *CIL_KEY_CALL;
 extern char *CIL_KEY_TUNABLE;
@@ -575,6 +578,8 @@ struct cil_nametypetransition {
 	struct cil_class *obj;
 	char *name_str;
 	struct cil_name *name;
+	char *name_match_str;
+	uint8_t name_match;
 	char *result_str;
 	void *result; /* type or alias */
 
diff --git a/libsepol/cil/src/cil_policy.c b/libsepol/cil/src/cil_policy.c
index feb97868..c8253818 100644
--- a/libsepol/cil/src/cil_policy.c
+++ b/libsepol/cil/src/cil_policy.c
@@ -1260,6 +1260,7 @@ static void cil_nametypetransition_to_policy(FILE *out, struct cil_nametypetrans
 	struct cil_name *name;
 	struct cil_list *class_list;
 	struct cil_list_item *i1;
+	const char *name_match_str = "";
 
 	src = trans->src;
 	tgt = trans->tgt;
@@ -1268,7 +1269,21 @@ static void cil_nametypetransition_to_policy(FILE *out, struct cil_nametypetrans
 
 	class_list = cil_expand_class(trans->obj);
 	cil_list_for_each(i1, class_list) {
-		fprintf(out, "type_transition %s %s : %s %s \"%s\";\n", src->fqn, tgt->fqn, DATUM(i1->data)->fqn, res->fqn, name->datum.fqn);
+		switch (trans->name_match) {
+		case NAME_TRANS_MATCH_EXACT:
+			name_match_str = "";
+			break;
+		case NAME_TRANS_MATCH_PREFIX:
+			name_match_str = " MATCH_PREFIX";
+			break;
+		case NAME_TRANS_MATCH_SUFFIX:
+			name_match_str = " MATCH_SUFFIX";
+			break;
+		default:
+			name_match_str = "???";
+			break;
+		}
+		fprintf(out, "type_transition %s %s : %s %s \"%s\"%s;\n", src->fqn, tgt->fqn, DATUM(i1->data)->fqn, res->fqn, name->datum.fqn, name_match_str);
 	}
 	cil_list_destroy(&class_list, CIL_FALSE);
 }
diff --git a/libsepol/cil/src/cil_resolve_ast.c b/libsepol/cil/src/cil_resolve_ast.c
index d2bfdc81..fbb0fdcc 100644
--- a/libsepol/cil/src/cil_resolve_ast.c
+++ b/libsepol/cil/src/cil_resolve_ast.c
@@ -668,6 +668,16 @@ int cil_resolve_nametypetransition(struct cil_tree_node *current, void *extra_ar
 		nametypetrans->name = (struct cil_name *)name_datum;
 	}
 
+	if (nametypetrans->name_match_str == CIL_KEY_MATCH_EXACT) {
+		nametypetrans->name_match = NAME_TRANS_MATCH_EXACT;
+	} else if (nametypetrans->name_match_str == CIL_KEY_MATCH_PREFIX) {
+		nametypetrans->name_match = NAME_TRANS_MATCH_PREFIX;
+	} else if (nametypetrans->name_match_str == CIL_KEY_MATCH_SUFFIX) {
+		nametypetrans->name_match = NAME_TRANS_MATCH_SUFFIX;
+	} else {
+		cil_tree_log(current, CIL_ERR, "Invalid name match type \"%s\"", nametypetrans->name_match_str);
+	}
+
 	rc = cil_resolve_name(current, nametypetrans->result_str, CIL_SYM_TYPES, extra_args, &result_datum);
 	if (rc != SEPOL_OK) {
 		goto exit;
diff --git a/libsepol/cil/src/cil_write_ast.c b/libsepol/cil/src/cil_write_ast.c
index b75784ef..d96f6c39 100644
--- a/libsepol/cil/src/cil_write_ast.c
+++ b/libsepol/cil/src/cil_write_ast.c
@@ -1168,6 +1168,8 @@ void cil_write_ast_node(FILE *out, struct cil_tree_node *node)
 		fprintf(out, "%s ", datum_or_str(DATUM(rule->tgt), rule->tgt_str));
 		fprintf(out, "%s ", datum_or_str(DATUM(rule->obj), rule->obj_str));
 		fprintf(out, "\"%s\" ", datum_or_str(DATUM(rule->name), rule->name_str));
+		if (rule->name_match != NAME_TRANS_MATCH_EXACT)
+			fprintf(out, "%s ", rule->name_match_str);
 		fprintf(out, "%s", datum_or_str(DATUM(rule->result), rule->result_str));
 		fprintf(out, ")\n");
 		break;
