Message ID | 20231205160210.1047687-1-juraj@jurajmarcin.com (mailing list archive) |
---|---|
State | New, archived |
Delegated to: | Petr Lautrbach |
Headers | show |
Series | [v6] checkpolicy,libsepol: add prefix/suffix matching to filename type transitions | expand |
On Tue, Dec 5, 2023 at 11:03 AM Juraj Marcin <juraj@jurajmarcin.com> wrote: > > Currently, filename transitions are stored separately from other type > enforcement rules and only support 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 implements the equivalent changes made by this kernel > patch [1]. > > This patch updates the policydb structure to contain prefix and suffix > filename transition tables along normal filename transitions table and > updates the code that accesses those tables. Furthermore, it adds > match_type attribute to module and CIL structures that store filename > transitions and updates functions that parse conf and CIL policy files. > > This patch does not significantly change the binary policy size when > prefix/suffix rules are not used. In addition, with prefix/suffix rules, > the number of filename transitions can be reduced, and therefore also > binary policy size can be reduced. > > Syntax of the new prefix/suffix filename transition rule: > > type_transition source_type target_type : class default_type object_name match_type; > > (typetransition source_type_id target_type_id class_id object_name match_type default_type_id) > > where match_type is either keyword "prefix" or "suffix" > > Examples: > > type_transition ta tb:CLASS01 tc "file01" prefix; > type_transition td te:CLASS01 tf "file02" suffix; > > (typetransition ta tb CLASS01 "file01" prefix td) > (typetransition td te CLASS01 "file02" suffix tf) > > In the kernel, the rules have the following order of priority, if no > matching rule is found, the code moves on to the next category: > - exact filename transitions, > - prefix filename transitions in the order of the longest prefix match, > - suffix filename transitions in the order of the longest suffix match. > This ensures the compatibility with older policies. > > [1]: https://lore.kernel.org/selinux/20231121122719.2332137-1-juraj@jurajmarcin.com/ > > Reviewed-by: Ondrej Mosnacek <omosnace@redhat.com> > Signed-off-by: Juraj Marcin <juraj@jurajmarcin.com> Needs the kernel part to be merged for this to be merged, but everything looks good to me. Acked-by: James Carter <jwcart2@gmail.com> Thanks, Jim > --- > v6: > - removed match_type_str from cil structures and moved match type > parsing to the build phase, as suggested by Jim > - add match string to map arg structs instead of match type, as > suggested by Jim > > v5: > - rebased to the latest main, fixed conflicts in libsepol/cil > > v4: > - rebased to the latest main, fixed conflicts in policydb_validate.c > - added syntax tests to checkpolicy and secilc as proposed by Christian > - retested with more test cases > > v3: > - reworked the solution from scratch, this time only adding the > prefix/suffix matching feature without moving filename transition > rules to the avtab > --- > checkpolicy/policy_define.c | 7 +- > checkpolicy/policy_define.h | 2 +- > checkpolicy/policy_parse.y | 13 ++- > checkpolicy/policy_scan.l | 4 + > checkpolicy/test/dismod.c | 14 ++- > checkpolicy/test/dispol.c | 14 ++- > checkpolicy/tests/policy_allonce.conf | 2 + > .../tests/policy_allonce.expected.conf | 8 ++ > .../tests/policy_allonce.expected_opt.conf | 8 ++ > checkpolicy/tests/policy_allonce_mls.conf | 2 + > .../tests/policy_allonce_mls.expected.conf | 8 ++ > .../policy_allonce_mls.expected_opt.conf | 8 ++ > libsepol/cil/src/cil.c | 5 + > libsepol/cil/src/cil_binary.c | 8 +- > libsepol/cil/src/cil_build_ast.c | 36 ++++++- > libsepol/cil/src/cil_copy_ast.c | 1 + > libsepol/cil/src/cil_internal.h | 3 + > libsepol/cil/src/cil_policy.c | 17 ++- > libsepol/cil/src/cil_write_ast.c | 10 ++ > libsepol/include/sepol/policydb/policydb.h | 23 +++- > libsepol/src/expand.c | 17 ++- > libsepol/src/kernel_to_cil.c | 24 ++++- > libsepol/src/kernel_to_conf.c | 24 ++++- > libsepol/src/link.c | 1 + > libsepol/src/module_to_cil.c | 21 +++- > libsepol/src/policydb.c | 100 ++++++++++++++---- > libsepol/src/policydb_validate.c | 26 ++++- > libsepol/src/write.c | 67 +++++++++--- > secilc/test/policy.cil | 4 + > 29 files changed, 407 insertions(+), 70 deletions(-) > > diff --git a/checkpolicy/policy_define.c b/checkpolicy/policy_define.c > index 260e609d..fb8325ee 100644 > --- a/checkpolicy/policy_define.c > +++ b/checkpolicy/policy_define.c > @@ -3159,7 +3159,7 @@ avrule_t *define_cond_filename_trans(void) > return COND_ERR; > } > > -int define_filename_trans(void) > +int define_filename_trans(uint32_t match_type) > { > char *id, *name = NULL; > type_set_t stypes, ttypes; > @@ -3261,7 +3261,7 @@ int define_filename_trans(void) > ebitmap_for_each_positive_bit(&e_ttypes, tnode, t) { > rc = policydb_filetrans_insert( > policydbp, s+1, t+1, c+1, name, > - NULL, otype, NULL > + NULL, otype, match_type, NULL > ); > if (rc != SEPOL_OK) { > if (rc == SEPOL_EEXIST) { > @@ -3279,7 +3279,7 @@ int define_filename_trans(void) > if (self) { > rc = policydb_filetrans_insert( > policydbp, s+1, s+1, c+1, name, > - NULL, otype, NULL > + NULL, otype, match_type, NULL > ); > if (rc != SEPOL_OK) { > if (rc == SEPOL_EEXIST) { > @@ -3317,6 +3317,7 @@ int define_filename_trans(void) > ftr->tclass = c + 1; > ftr->otype = otype; > ftr->flags = self ? RULE_SELF : 0; > + ftr->match_type = match_type; > } > > free(name); > diff --git a/checkpolicy/policy_define.h b/checkpolicy/policy_define.h > index 075b048d..05869346 100644 > --- a/checkpolicy/policy_define.h > +++ b/checkpolicy/policy_define.h > @@ -57,7 +57,7 @@ int define_role_trans(int class_specified); > int define_role_types(void); > int define_role_attr(void); > int define_roleattribute(void); > -int define_filename_trans(void); > +int define_filename_trans(uint32_t match_type); > int define_sens(void); > int define_te_avtab(int which); > int define_te_avtab_extended_perms(int which); > diff --git a/checkpolicy/policy_parse.y b/checkpolicy/policy_parse.y > index 356626e2..ee4be4ea 100644 > --- a/checkpolicy/policy_parse.y > +++ b/checkpolicy/policy_parse.y > @@ -153,6 +153,7 @@ typedef int (* require_func_t)(int pass); > %token FILESYSTEM > %token DEFAULT_USER DEFAULT_ROLE DEFAULT_TYPE DEFAULT_RANGE > %token LOW_HIGH LOW HIGH GLBLUB > +%token PREFIX SUFFIX > > %left OR > %left XOR > @@ -410,6 +411,12 @@ cond_rule_def : cond_transition_def > { $$ = NULL; } > ; > cond_transition_def : TYPE_TRANSITION names names ':' names identifier filename ';' > + { $$ = define_cond_filename_trans() ; > + if ($$ == COND_ERR) return -1;} > + | TYPE_TRANSITION names names ':' names identifier filename PREFIX ';' > + { $$ = define_cond_filename_trans() ; > + if ($$ == COND_ERR) return -1;} > + | TYPE_TRANSITION names names ':' names identifier filename SUFFIX ';' > { $$ = define_cond_filename_trans() ; > if ($$ == COND_ERR) return -1;} > | TYPE_TRANSITION names names ':' names identifier ';' > @@ -449,7 +456,11 @@ cond_dontaudit_def : DONTAUDIT names names ':' names names ';' > ; > ; > transition_def : TYPE_TRANSITION names names ':' names identifier filename ';' > - {if (define_filename_trans()) return -1; } > + {if (define_filename_trans(FILENAME_TRANS_MATCH_EXACT)) return -1; } > + | TYPE_TRANSITION names names ':' names identifier filename PREFIX ';' > + {if (define_filename_trans(FILENAME_TRANS_MATCH_PREFIX)) return -1; } > + | TYPE_TRANSITION names names ':' names identifier filename SUFFIX ';' > + {if (define_filename_trans(FILENAME_TRANS_MATCH_SUFFIX)) return -1; } > | TYPE_TRANSITION names names ':' names identifier ';' > {if (define_compute_type(AVRULE_TRANSITION)) return -1;} > | TYPE_MEMBER names names ':' names identifier ';' > diff --git a/checkpolicy/policy_scan.l b/checkpolicy/policy_scan.l > index c998ff8b..0780ef15 100644 > --- a/checkpolicy/policy_scan.l > +++ b/checkpolicy/policy_scan.l > @@ -270,6 +270,10 @@ low | > LOW { return(LOW); } > glblub | > GLBLUB { return(GLBLUB); } > +PREFIX | > +prefix { return(PREFIX); } > +SUFFIX | > +suffix { return(SUFFIX); } > "/"[^ \n\r\t\f]* { return(PATH); } > \""/"[^\"\n]*\" { return(QPATH); } > \"[^"/"\"\n]+\" { return(FILENAME); } > diff --git a/checkpolicy/test/dismod.c b/checkpolicy/test/dismod.c > index 9f4a669b..931af9da 100644 > --- a/checkpolicy/test/dismod.c > +++ b/checkpolicy/test/dismod.c > @@ -571,13 +571,25 @@ static void display_role_allow(role_allow_rule_t * ra, policydb_t * p, FILE * fp > > static void display_filename_trans(filename_trans_rule_t * tr, policydb_t * p, FILE * fp) > { > + const char *match_str = ""; > fprintf(fp, "filename transition"); > for (; tr; tr = tr->next) { > display_type_set(&tr->stypes, 0, p, fp); > display_type_set(&tr->ttypes, 0, p, fp); > display_id(p, fp, SYM_CLASSES, tr->tclass - 1, ":"); > display_id(p, fp, SYM_TYPES, tr->otype - 1, ""); > - fprintf(fp, " %s\n", tr->name); > + switch (tr->match_type) { > + case FILENAME_TRANS_MATCH_EXACT: > + match_str = ""; > + break; > + case FILENAME_TRANS_MATCH_PREFIX: > + match_str = " prefix"; > + break; > + case FILENAME_TRANS_MATCH_SUFFIX: > + match_str = " suffix"; > + break; > + } > + fprintf(fp, " %s%s\n", tr->name, match_str); > } > } > > diff --git a/checkpolicy/test/dispol.c b/checkpolicy/test/dispol.c > index 944ef7ec..999c26ca 100644 > --- a/checkpolicy/test/dispol.c > +++ b/checkpolicy/test/dispol.c > @@ -458,6 +458,7 @@ static void display_role_trans(policydb_t *p, FILE *fp) > > struct filenametr_display_args { > policydb_t *p; > + const char *match_str; > FILE *fp; > }; > > @@ -472,6 +473,7 @@ static int filenametr_display(hashtab_key_t key, > FILE *fp = args->fp; > ebitmap_node_t *node; > uint32_t bit; > + const char *match_str = args->match_str; > > do { > ebitmap_for_each_positive_bit(&ftdatum->stypes, node, bit) { > @@ -479,7 +481,7 @@ static int filenametr_display(hashtab_key_t key, > display_id(p, fp, SYM_TYPES, ft->ttype - 1, ""); > display_id(p, fp, SYM_CLASSES, ft->tclass - 1, ":"); > display_id(p, fp, SYM_TYPES, ftdatum->otype - 1, ""); > - fprintf(fp, " %s\n", ft->name); > + fprintf(fp, " %s%s\n", ft->name, match_str); > } > ftdatum = ftdatum->next; > } while (ftdatum); > @@ -495,7 +497,15 @@ static void display_filename_trans(policydb_t *p, FILE *fp) > fprintf(fp, "filename_trans rules:\n"); > args.p = p; > args.fp = fp; > - hashtab_map(p->filename_trans, filenametr_display, &args); > + args.match_str = ""; > + hashtab_map(p->filename_trans[FILENAME_TRANS_MATCH_EXACT], > + filenametr_display, &args); > + args.match_str = " prefix"; > + hashtab_map(p->filename_trans[FILENAME_TRANS_MATCH_PREFIX], > + filenametr_display, &args); > + args.match_str = " suffix"; > + hashtab_map(p->filename_trans[FILENAME_TRANS_MATCH_SUFFIX], > + filenametr_display, &args); > } > > static int menu(void) > diff --git a/checkpolicy/tests/policy_allonce.conf b/checkpolicy/tests/policy_allonce.conf > index 34e6402d..fb0a0172 100644 > --- a/checkpolicy/tests/policy_allonce.conf > +++ b/checkpolicy/tests/policy_allonce.conf > @@ -30,6 +30,8 @@ tunable TUNABLE1 false; > tunable TUNABLE2 true; > type_transition TYPE1 TYPE2 : CLASS1 TYPE3; > type_transition { TYPE1 TYPE2 } { TYPE3 TYPE4 } : CLASS1 TYPE1 "FILENAME"; > +type_transition { TYPE1 TYPE2 } { TYPE3 TYPE4 } : CLASS1 TYPE1 "FILENAME" prefix; > +type_transition { TYPE1 TYPE2 } { TYPE3 TYPE4 } : CLASS1 TYPE1 "FILENAME" suffix; > type_member TYPE1 TYPE2 : CLASS1 TYPE2; > type_change TYPE1 TYPE2 : CLASS1 TYPE3; > allow TYPE1 self : CLASS1 { PERM1 }; > diff --git a/checkpolicy/tests/policy_allonce.expected.conf b/checkpolicy/tests/policy_allonce.expected.conf > index 63739e1f..e3db7593 100644 > --- a/checkpolicy/tests/policy_allonce.expected.conf > +++ b/checkpolicy/tests/policy_allonce.expected.conf > @@ -40,9 +40,17 @@ dontauditxperm TYPE1 TYPE2:CLASS1 ioctl { 0x3 }; > type_transition TYPE1 TYPE2:CLASS1 TYPE3; > type_member TYPE1 TYPE2:CLASS1 TYPE2; > type_change TYPE1 TYPE2:CLASS1 TYPE3; > +type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME" prefix; > +type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME" suffix; > type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME"; > +type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME" prefix; > +type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME" suffix; > type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME"; > +type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME" prefix; > +type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME" suffix; > type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME"; > +type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME" prefix; > +type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME" suffix; > type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME"; > if (BOOL1) { > } else { > diff --git a/checkpolicy/tests/policy_allonce.expected_opt.conf b/checkpolicy/tests/policy_allonce.expected_opt.conf > index 1c969961..94eaf37a 100644 > --- a/checkpolicy/tests/policy_allonce.expected_opt.conf > +++ b/checkpolicy/tests/policy_allonce.expected_opt.conf > @@ -40,9 +40,17 @@ dontauditxperm TYPE1 TYPE2:CLASS1 ioctl { 0x3 }; > type_transition TYPE1 TYPE2:CLASS1 TYPE3; > type_member TYPE1 TYPE2:CLASS1 TYPE2; > type_change TYPE1 TYPE2:CLASS1 TYPE3; > +type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME" prefix; > +type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME" suffix; > type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME"; > +type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME" prefix; > +type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME" suffix; > type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME"; > +type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME" prefix; > +type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME" suffix; > type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME"; > +type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME" prefix; > +type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME" suffix; > type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME"; > if (BOOL1) { > } else { > diff --git a/checkpolicy/tests/policy_allonce_mls.conf b/checkpolicy/tests/policy_allonce_mls.conf > index c88616b3..2ba88a00 100644 > --- a/checkpolicy/tests/policy_allonce_mls.conf > +++ b/checkpolicy/tests/policy_allonce_mls.conf > @@ -41,6 +41,8 @@ tunable TUNABLE1 false; > tunable TUNABLE2 true; > type_transition TYPE1 TYPE2 : CLASS1 TYPE3; > type_transition { TYPE1 TYPE2 } { TYPE3 TYPE4 } : CLASS1 TYPE1 "FILENAME"; > +type_transition { TYPE1 TYPE2 } { TYPE3 TYPE4 } : CLASS1 TYPE1 "FILENAME" prefix; > +type_transition { TYPE1 TYPE2 } { TYPE3 TYPE4 } : CLASS1 TYPE1 "FILENAME" suffix; > type_member TYPE1 TYPE2 : CLASS1 TYPE2; > type_change TYPE1 TYPE2 : CLASS1 TYPE3; > range_transition TYPE1 TYPE2 : CLASS1 s1:c0.c1; > diff --git a/checkpolicy/tests/policy_allonce_mls.expected.conf b/checkpolicy/tests/policy_allonce_mls.expected.conf > index 87c36f92..d390aac0 100644 > --- a/checkpolicy/tests/policy_allonce_mls.expected.conf > +++ b/checkpolicy/tests/policy_allonce_mls.expected.conf > @@ -51,9 +51,17 @@ dontauditxperm TYPE1 TYPE2:CLASS1 ioctl { 0x3 }; > type_transition TYPE1 TYPE2:CLASS1 TYPE3; > type_member TYPE1 TYPE2:CLASS1 TYPE2; > type_change TYPE1 TYPE2:CLASS1 TYPE3; > +type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME" prefix; > +type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME" suffix; > type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME"; > +type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME" prefix; > +type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME" suffix; > type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME"; > +type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME" prefix; > +type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME" suffix; > type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME"; > +type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME" prefix; > +type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME" suffix; > type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME"; > range_transition TYPE1 TYPE2:CLASS1 s1:c0,c1 - s1:c0,c1; > if (BOOL1) { > diff --git a/checkpolicy/tests/policy_allonce_mls.expected_opt.conf b/checkpolicy/tests/policy_allonce_mls.expected_opt.conf > index 38176166..ef683070 100644 > --- a/checkpolicy/tests/policy_allonce_mls.expected_opt.conf > +++ b/checkpolicy/tests/policy_allonce_mls.expected_opt.conf > @@ -51,9 +51,17 @@ dontauditxperm TYPE1 TYPE2:CLASS1 ioctl { 0x3 }; > type_transition TYPE1 TYPE2:CLASS1 TYPE3; > type_member TYPE1 TYPE2:CLASS1 TYPE2; > type_change TYPE1 TYPE2:CLASS1 TYPE3; > +type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME" prefix; > +type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME" suffix; > type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME"; > +type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME" prefix; > +type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME" suffix; > type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME"; > +type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME" prefix; > +type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME" suffix; > type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME"; > +type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME" prefix; > +type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME" suffix; > type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME"; > range_transition TYPE1 TYPE2:CLASS1 s1:c0,c1 - s1:c0,c1; > if (BOOL1) { > diff --git a/libsepol/cil/src/cil.c b/libsepol/cil/src/cil.c > index 067e28a6..fd1f7bd8 100644 > --- a/libsepol/cil/src/cil.c > +++ b/libsepol/cil/src/cil.c > @@ -97,6 +97,8 @@ char *CIL_KEY_TUNABLEIF; > char *CIL_KEY_ALLOW; > char *CIL_KEY_DONTAUDIT; > char *CIL_KEY_TYPETRANSITION; > +char *CIL_KEY_PREFIX; > +char *CIL_KEY_SUFFIX; > char *CIL_KEY_TYPECHANGE; > char *CIL_KEY_CALL; > char *CIL_KEY_TUNABLE; > @@ -269,6 +271,8 @@ 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_PREFIX = cil_strpool_add("prefix"); > + CIL_KEY_SUFFIX = cil_strpool_add("suffix"); > CIL_KEY_TYPECHANGE = cil_strpool_add("typechange"); > CIL_KEY_CALL = cil_strpool_add("call"); > CIL_KEY_TUNABLE = cil_strpool_add("tunable"); > @@ -2461,6 +2465,7 @@ void cil_nametypetransition_init(struct cil_nametypetransition **nametypetrans) > (*nametypetrans)->obj = NULL; > (*nametypetrans)->name_str = NULL; > (*nametypetrans)->name = NULL; > + (*nametypetrans)->match_type = FILENAME_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 a8e3616a..75a9e064 100644 > --- a/libsepol/cil/src/cil_binary.c > +++ b/libsepol/cil/src/cil_binary.c > @@ -1168,7 +1168,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, uint32_t match_type, > type_datum_t *sepol_result) > { > int rc; > @@ -1183,7 +1183,7 @@ static int __cil_typetransition_to_avtab_helper(policydb_t *pdb, > rc = policydb_filetrans_insert( > pdb, sepol_src->s.value, sepol_tgt->s.value, > sepol_obj->s.value, name, NULL, > - sepol_result->s.value, &otype > + sepol_result->s.value, match_type, &otype > ); > if (rc != SEPOL_OK) { > if (rc == SEPOL_EEXIST) { > @@ -1252,7 +1252,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->match_type, sepol_result > ); > if (rc != SEPOL_OK) goto exit; > } > @@ -1270,7 +1270,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->match_type, 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 be260a31..d91b8d20 100644 > --- a/libsepol/cil/src/cil_build_ast.c > +++ b/libsepol/cil/src/cil_build_ast.c > @@ -3386,10 +3386,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; > @@ -3405,12 +3406,22 @@ 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 = parse_current->next->next->next->next->next->data; > + } > } > } > > @@ -3426,7 +3437,22 @@ int cil_gen_typetransition(struct cil_db *db, struct cil_tree_node *parse_curren > nametypetrans->obj_str = s3; > nametypetrans->name_str = s4; > nametypetrans->name = cil_gen_declared_string(db, s4, ast_node); > - nametypetrans->result_str = s5; > + if (s6) { > + if (s5 == CIL_KEY_PREFIX) { > + nametypetrans->match_type = FILENAME_TRANS_MATCH_PREFIX; > + } else if (s5 == CIL_KEY_SUFFIX) { > + nametypetrans->match_type = FILENAME_TRANS_MATCH_SUFFIX; > + } else { > + rc = SEPOL_ERR; > + goto exit; > + } > + nametypetrans->result_str = s6; > + } else { > + nametypetrans->result_str = s5; > + } > + > + ast_node->data = nametypetrans; > + ast_node->flavor = CIL_NAMETYPETRANSITION; > } else { > struct cil_type_rule *rule = NULL; > cil_type_rule_init(&rule); > diff --git a/libsepol/cil/src/cil_copy_ast.c b/libsepol/cil/src/cil_copy_ast.c > index 1507edb4..64e2cacd 100644 > --- a/libsepol/cil/src/cil_copy_ast.c > +++ b/libsepol/cil/src/cil_copy_ast.c > @@ -723,6 +723,7 @@ int cil_copy_nametypetransition(__attribute__((unused)) struct cil_db *db, void > new->obj_str = orig->obj_str; > new->name_str = orig->name_str; > new->name = orig->name; > + new->match_type = orig->match_type; > new->result_str = orig->result_str; > > > diff --git a/libsepol/cil/src/cil_internal.h b/libsepol/cil/src/cil_internal.h > index 47b67c89..8c9f1151 100644 > --- a/libsepol/cil/src/cil_internal.h > +++ b/libsepol/cil/src/cil_internal.h > @@ -114,6 +114,8 @@ extern char *CIL_KEY_TUNABLEIF; > extern char *CIL_KEY_ALLOW; > extern char *CIL_KEY_DONTAUDIT; > extern char *CIL_KEY_TYPETRANSITION; > +extern char *CIL_KEY_PREFIX; > +extern char *CIL_KEY_SUFFIX; > extern char *CIL_KEY_TYPECHANGE; > extern char *CIL_KEY_CALL; > extern char *CIL_KEY_TUNABLE; > @@ -588,6 +590,7 @@ struct cil_nametypetransition { > struct cil_class *obj; > char *name_str; > struct cil_symtab_datum *name; > + uint32_t match_type; > 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 e9a8f75d..b7f096b7 100644 > --- a/libsepol/cil/src/cil_policy.c > +++ b/libsepol/cil/src/cil_policy.c > @@ -1259,15 +1259,30 @@ static void cil_nametypetransition_to_policy(FILE *out, struct cil_nametypetrans > struct cil_symtab_datum *src, *tgt, *name, *res; > struct cil_list *class_list; > struct cil_list_item *i1; > + const char *match_type_str = ""; > > src = trans->src; > tgt = trans->tgt; > name = trans->name; > res = trans->result; > + switch (trans->match_type) { > + case FILENAME_TRANS_MATCH_EXACT: > + match_type_str = ""; > + break; > + case FILENAME_TRANS_MATCH_PREFIX: > + match_type_str = " prefix"; > + break; > + case FILENAME_TRANS_MATCH_SUFFIX: > + match_type_str = " suffix"; > + break; > + default: > + match_type_str = " ???"; > + break; > + } > > 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->fqn); > + fprintf(out, "type_transition %s %s : %s %s \"%s\"%s;\n", src->fqn, tgt->fqn, DATUM(i1->data)->fqn, res->fqn, name->fqn, match_type_str); > } > cil_list_destroy(&class_list, CIL_FALSE); > } > diff --git a/libsepol/cil/src/cil_write_ast.c b/libsepol/cil/src/cil_write_ast.c > index f4f9f167..dbf01f5a 100644 > --- a/libsepol/cil/src/cil_write_ast.c > +++ b/libsepol/cil/src/cil_write_ast.c > @@ -1217,6 +1217,16 @@ void cil_write_ast_node(FILE *out, struct cil_tree_node *node) > } else { > fprintf(out, "%s ", rule->name_str); > } > + switch (rule->match_type) { > + case FILENAME_TRANS_MATCH_EXACT: > + break; > + case FILENAME_TRANS_MATCH_PREFIX: > + fprintf(out, "prefix "); > + break; > + case FILENAME_TRANS_MATCH_SUFFIX: > + fprintf(out, "suffix "); > + break; > + } > fprintf(out, "%s", datum_or_str(DATUM(rule->result), rule->result_str)); > fprintf(out, ")\n"); > break; > diff --git a/libsepol/include/sepol/policydb/policydb.h b/libsepol/include/sepol/policydb/policydb.h > index 6682069e..5ee0fa16 100644 > --- a/libsepol/include/sepol/policydb/policydb.h > +++ b/libsepol/include/sepol/policydb/policydb.h > @@ -321,6 +321,8 @@ typedef struct filename_trans_rule { > uint32_t tclass; > char *name; > uint32_t otype; /* new type */ > + /* name match type, values from enum filename_trans_match_type */ > + uint32_t match_type; > struct filename_trans_rule *next; > } filename_trans_rule_t; > > @@ -423,6 +425,14 @@ typedef struct genfs { > /* OCON_NUM needs to be the largest index in any platform's ocontext array */ > #define OCON_NUM 9 > > +/* filename transitions table array indices */ > +enum filename_trans_match_type { > + FILENAME_TRANS_MATCH_EXACT, > + FILENAME_TRANS_MATCH_PREFIX, > + FILENAME_TRANS_MATCH_SUFFIX, > + FILENAME_TRANS_MATCH_NUM, > +}; > + > /* section: module information */ > > /* scope_index_t holds all of the symbols that are in scope in a > @@ -593,8 +603,8 @@ typedef struct policydb { > hashtab_t range_tr; > > /* file transitions with the last path component */ > - hashtab_t filename_trans; > - uint32_t filename_trans_count; > + hashtab_t filename_trans[FILENAME_TRANS_MATCH_NUM]; > + uint32_t filename_trans_exact_count; > > ebitmap_t *type_attr_map; > > @@ -657,7 +667,8 @@ extern int policydb_sort_ocontexts(policydb_t *p); > extern int policydb_filetrans_insert(policydb_t *p, uint32_t stype, > uint32_t ttype, uint32_t tclass, > const char *name, char **name_alloc, > - uint32_t otype, uint32_t *present_otype); > + uint32_t otype, uint32_t match_type, > + uint32_t *present_otype); > > /* Deprecated */ > extern int policydb_context_isvalid(const policydb_t * p, > @@ -758,10 +769,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_PREFIX_SUFFIX 34 /* prefix and suffix filename transitions */ > > /* 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_PREFIX_SUFFIX > > /* Module versions and specific changes*/ > #define MOD_POLICYDB_VERSION_BASE 4 > @@ -784,9 +796,10 @@ extern int policydb_set_target_platform(policydb_t *p, int platform); > #define MOD_POLICYDB_VERSION_INFINIBAND 19 > #define MOD_POLICYDB_VERSION_GLBLUB 20 > #define MOD_POLICYDB_VERSION_SELF_TYPETRANS 21 > +#define MOD_POLICYDB_VERSION_PREFIX_SUFFIX 22 > > #define MOD_POLICYDB_VERSION_MIN MOD_POLICYDB_VERSION_BASE > -#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_SELF_TYPETRANS > +#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_PREFIX_SUFFIX > > #define POLICYDB_CONFIG_MLS 1 > > diff --git a/libsepol/src/expand.c b/libsepol/src/expand.c > index e63414b1..93170a14 100644 > --- a/libsepol/src/expand.c > +++ b/libsepol/src/expand.c > @@ -1419,18 +1419,31 @@ static int expand_filename_trans_helper(expand_state_t *state, > rc = policydb_filetrans_insert( > state->out, s + 1, t + 1, > rule->tclass, rule->name, > - NULL, mapped_otype, &present_otype > + NULL, mapped_otype, rule->match_type, &present_otype > ); > if (rc == SEPOL_EEXIST) { > /* duplicate rule, ignore */ > if (present_otype == mapped_otype) > return 0; > > - ERR(state->handle, "Conflicting name-based type_transition %s %s:%s \"%s\": %s vs %s", > + const char *match_str = ""; > + switch (rule->match_type) { > + case FILENAME_TRANS_MATCH_EXACT: > + match_str = ""; > + break; > + case FILENAME_TRANS_MATCH_PREFIX: > + match_str = " prefix"; > + break; > + case FILENAME_TRANS_MATCH_SUFFIX: > + match_str = " suffix"; > + break; > + } > + ERR(state->handle, "Conflicting name-based type_transition %s %s:%s \"%s\"%s: %s vs %s", > state->out->p_type_val_to_name[s], > state->out->p_type_val_to_name[t], > state->out->p_class_val_to_name[rule->tclass - 1], > rule->name, > + match_str, > state->out->p_type_val_to_name[present_otype - 1], > state->out->p_type_val_to_name[mapped_otype - 1]); > return -1; > diff --git a/libsepol/src/kernel_to_cil.c b/libsepol/src/kernel_to_cil.c > index 8ec79749..5dd6cfdc 100644 > --- a/libsepol/src/kernel_to_cil.c > +++ b/libsepol/src/kernel_to_cil.c > @@ -1882,6 +1882,7 @@ exit: > > struct map_filename_trans_args { > struct policydb *pdb; > + const char *match_str; > struct strs *strs; > }; > > @@ -1896,6 +1897,7 @@ static int map_filename_trans_to_str(hashtab_key_t key, void *data, void *arg) > struct ebitmap_node *node; > uint32_t bit; > int rc; > + const char *match_str = map_args->match_str; > > tgt = pdb->p_type_val_to_name[ft->ttype - 1]; > class = pdb->p_class_val_to_name[ft->tclass - 1]; > @@ -1906,8 +1908,8 @@ static int map_filename_trans_to_str(hashtab_key_t key, void *data, void *arg) > ebitmap_for_each_positive_bit(&datum->stypes, node, bit) { > src = pdb->p_type_val_to_name[bit]; > rc = strs_create_and_add(strs, > - "(typetransition %s %s %s \"%s\" %s)", > - 5, src, tgt, class, filename, new); > + "(typetransition %s %s %s \"%s\"%s %s)", > + 6, src, tgt, class, filename, match_str, new); > if (rc) > return rc; > } > @@ -1932,7 +1934,23 @@ static int write_filename_trans_rules_to_cil(FILE *out, struct policydb *pdb) > args.pdb = pdb; > args.strs = strs; > > - rc = hashtab_map(pdb->filename_trans, map_filename_trans_to_str, &args); > + args.match_str = ""; > + rc = hashtab_map(pdb->filename_trans[FILENAME_TRANS_MATCH_EXACT], > + map_filename_trans_to_str, &args); > + if (rc != 0) { > + goto exit; > + } > + > + args.match_str = " prefix"; > + rc = hashtab_map(pdb->filename_trans[FILENAME_TRANS_MATCH_PREFIX], > + map_filename_trans_to_str, &args); > + if (rc != 0) { > + goto exit; > + } > + > + args.match_str = " suffix"; > + rc = hashtab_map(pdb->filename_trans[FILENAME_TRANS_MATCH_SUFFIX], > + map_filename_trans_to_str, &args); > if (rc != 0) { > goto exit; > } > diff --git a/libsepol/src/kernel_to_conf.c b/libsepol/src/kernel_to_conf.c > index b5b530d6..d3631d8d 100644 > --- a/libsepol/src/kernel_to_conf.c > +++ b/libsepol/src/kernel_to_conf.c > @@ -1846,6 +1846,7 @@ exit: > > struct map_filename_trans_args { > struct policydb *pdb; > + const char *match_str; > struct strs *strs; > }; > > @@ -1860,6 +1861,7 @@ static int map_filename_trans_to_str(hashtab_key_t key, void *data, void *arg) > struct ebitmap_node *node; > uint32_t bit; > int rc; > + const char *match_str = map_args->match_str; > > tgt = pdb->p_type_val_to_name[ft->ttype - 1]; > class = pdb->p_class_val_to_name[ft->tclass - 1]; > @@ -1870,8 +1872,8 @@ static int map_filename_trans_to_str(hashtab_key_t key, void *data, void *arg) > ebitmap_for_each_positive_bit(&datum->stypes, node, bit) { > src = pdb->p_type_val_to_name[bit]; > rc = strs_create_and_add(strs, > - "type_transition %s %s:%s %s \"%s\";", > - 5, src, tgt, class, new, filename); > + "type_transition %s %s:%s %s \"%s\"%s;", > + 6, src, tgt, class, new, filename, match_str); > if (rc) > return rc; > } > @@ -1896,7 +1898,23 @@ static int write_filename_trans_rules_to_conf(FILE *out, struct policydb *pdb) > args.pdb = pdb; > args.strs = strs; > > - rc = hashtab_map(pdb->filename_trans, map_filename_trans_to_str, &args); > + args.match_str = ""; > + rc = hashtab_map(pdb->filename_trans[FILENAME_TRANS_MATCH_EXACT], > + map_filename_trans_to_str, &args); > + if (rc != 0) { > + goto exit; > + } > + > + args.match_str = " prefix"; > + rc = hashtab_map(pdb->filename_trans[FILENAME_TRANS_MATCH_PREFIX], > + map_filename_trans_to_str, &args); > + if (rc != 0) { > + goto exit; > + } > + > + args.match_str = " suffix"; > + rc = hashtab_map(pdb->filename_trans[FILENAME_TRANS_MATCH_SUFFIX], > + map_filename_trans_to_str, &args); > if (rc != 0) { > goto exit; > } > diff --git a/libsepol/src/link.c b/libsepol/src/link.c > index 3b7742bc..f432087f 100644 > --- a/libsepol/src/link.c > +++ b/libsepol/src/link.c > @@ -1440,6 +1440,7 @@ static int copy_filename_trans_list(filename_trans_rule_t * list, > new_rule->name = strdup(cur->name); > if (!new_rule->name) > goto err; > + new_rule->match_type = cur->match_type; > > if (type_set_or_convert(&cur->stypes, &new_rule->stypes, module) || > type_set_or_convert(&cur->ttypes, &new_rule->ttypes, module)) > diff --git a/libsepol/src/module_to_cil.c b/libsepol/src/module_to_cil.c > index ee22dbbd..a90ee85d 100644 > --- a/libsepol/src/module_to_cil.c > +++ b/libsepol/src/module_to_cil.c > @@ -1611,6 +1611,7 @@ static int filename_trans_to_cil(int indent, struct policydb *pdb, struct filena > unsigned int ttype; > struct type_set *ts; > struct filename_trans_rule *rule; > + const char *match_str = ""; > > for (rule = rules; rule != NULL; rule = rule->next) { > ts = &rule->stypes; > @@ -1625,19 +1626,31 @@ static int filename_trans_to_cil(int indent, struct policydb *pdb, struct filena > goto exit; > } > > + switch (rule->match_type) { > + case FILENAME_TRANS_MATCH_EXACT: > + match_str = ""; > + break; > + case FILENAME_TRANS_MATCH_PREFIX: > + match_str = " prefix"; > + break; > + case FILENAME_TRANS_MATCH_SUFFIX: > + match_str = " suffix"; > + break; > + } > + > for (stype = 0; stype < num_stypes; stype++) { > for (ttype = 0; ttype < num_ttypes; ttype++) { > - cil_println(indent, "(typetransition %s %s %s \"%s\" %s)", > + cil_println(indent, "(typetransition %s %s %s \"%s\"%s %s)", > stypes[stype], ttypes[ttype], > pdb->p_class_val_to_name[rule->tclass - 1], > - rule->name, > + rule->name, match_str, > pdb->p_type_val_to_name[rule->otype - 1]); > } > if (rule->flags & RULE_SELF) { > - cil_println(indent, "(typetransition %s self %s \"%s\" %s)", > + cil_println(indent, "(typetransition %s self %s \"%s\"%s %s)", > stypes[stype], > pdb->p_class_val_to_name[rule->tclass - 1], > - rule->name, > + rule->name, match_str, > pdb->p_type_val_to_name[rule->otype - 1]); > } > } > diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c > index 6ba4f916..a1796844 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_PREFIX_SUFFIX, > + .sym_num = SYM_NUM, > + .ocon_num = OCON_IBENDPORT + 1, > + .target_platform = SEPOL_TARGET_SELINUX, > + }, > { > .type = POLICY_BASE, > .version = MOD_POLICYDB_VERSION_BASE, > @@ -334,6 +341,13 @@ static const struct policydb_compat_info policydb_compat[] = { > .ocon_num = OCON_IBENDPORT + 1, > .target_platform = SEPOL_TARGET_SELINUX, > }, > + { > + .type = POLICY_BASE, > + .version = MOD_POLICYDB_VERSION_PREFIX_SUFFIX, > + .sym_num = SYM_NUM, > + .ocon_num = OCON_IBENDPORT + 1, > + .target_platform = SEPOL_TARGET_SELINUX, > + }, > { > .type = POLICY_MOD, > .version = MOD_POLICYDB_VERSION_BASE, > @@ -460,6 +474,13 @@ static const struct policydb_compat_info policydb_compat[] = { > .ocon_num = 0, > .target_platform = SEPOL_TARGET_SELINUX, > }, > + { > + .type = POLICY_MOD, > + .version = MOD_POLICYDB_VERSION_PREFIX_SUFFIX, > + .sym_num = SYM_NUM, > + .ocon_num = 0, > + .target_platform = SEPOL_TARGET_SELINUX, > + }, > }; > > #if 0 > @@ -909,10 +930,14 @@ int policydb_init(policydb_t * p) > if (rc) > goto err; > > - p->filename_trans = hashtab_create(filenametr_hash, filenametr_cmp, (1 << 10)); > - if (!p->filename_trans) { > - rc = -ENOMEM; > - goto err; > + for (i = 0; i < FILENAME_TRANS_MATCH_NUM; i++) { > + p->filename_trans[i] = hashtab_create(filenametr_hash, > + filenametr_cmp, > + (1 << 10)); > + if (!p->filename_trans[i]) { > + rc = -ENOMEM; > + goto err; > + } > } > > p->range_tr = hashtab_create(rangetr_hash, rangetr_cmp, 256); > @@ -926,7 +951,9 @@ int policydb_init(policydb_t * p) > > return 0; > err: > - hashtab_destroy(p->filename_trans); > + for (i = 0; i < FILENAME_TRANS_MATCH_NUM; i++) { > + hashtab_destroy(p->filename_trans[i]); > + } > hashtab_destroy(p->range_tr); > for (i = 0; i < SYM_NUM; i++) { > hashtab_destroy(p->symtab[i].table); > @@ -1564,8 +1591,10 @@ void policydb_destroy(policydb_t * p) > if (lra) > free(lra); > > - hashtab_map(p->filename_trans, filenametr_destroy, NULL); > - hashtab_destroy(p->filename_trans); > + for (i = 0; i < FILENAME_TRANS_MATCH_NUM; i++) { > + hashtab_map(p->filename_trans[i], filenametr_destroy, NULL); > + hashtab_destroy(p->filename_trans[i]); > + } > > hashtab_map(p->range_tr, range_tr_destroy, NULL); > hashtab_destroy(p->range_tr); > @@ -2566,7 +2595,7 @@ static int role_allow_read(role_allow_t ** r, struct policy_file *fp) > int policydb_filetrans_insert(policydb_t *p, uint32_t stype, uint32_t ttype, > uint32_t tclass, const char *name, > char **name_alloc, uint32_t otype, > - uint32_t *present_otype) > + uint32_t match_type, uint32_t *present_otype) > { > filename_trans_key_t *ft, key; > filename_trans_datum_t *datum, *last; > @@ -2576,7 +2605,8 @@ int policydb_filetrans_insert(policydb_t *p, uint32_t stype, uint32_t ttype, > key.name = (char *)name; > > last = NULL; > - datum = hashtab_search(p->filename_trans, (hashtab_key_t)&key); > + datum = hashtab_search(p->filename_trans[match_type], > + (hashtab_key_t)&key); > while (datum) { > if (ebitmap_get_bit(&datum->stypes, stype - 1)) { > if (present_otype) > @@ -2624,7 +2654,8 @@ int policydb_filetrans_insert(policydb_t *p, uint32_t stype, uint32_t ttype, > ft->tclass = tclass; > ft->name = name_dup; > > - if (hashtab_insert(p->filename_trans, (hashtab_key_t)ft, > + if (hashtab_insert(p->filename_trans[match_type], > + (hashtab_key_t)ft, > (hashtab_datum_t)datum)) { > free(name_dup); > free(datum); > @@ -2634,7 +2665,14 @@ int policydb_filetrans_insert(policydb_t *p, uint32_t stype, uint32_t ttype, > } > } > > - p->filename_trans_count++; > + /* > + * We need to keep track of the number of exact match filename > + * transitions for writing them in uncompressed format in older binary > + * policy versions. Other match types were not supported back then, so > + * it is not needed. > + */ > + if (match_type == FILENAME_TRANS_MATCH_EXACT) > + p->filename_trans_exact_count++; > return ebitmap_set_bit(&datum->stypes, stype - 1, 1); > } > > @@ -2665,8 +2703,9 @@ static int filename_trans_read_one_compat(policydb_t *p, struct policy_file *fp) > tclass = le32_to_cpu(buf[2]); > otype = le32_to_cpu(buf[3]); > > + // This version does not contain other than exact filename transitions > rc = policydb_filetrans_insert(p, stype, ttype, tclass, name, &name, > - otype, NULL); > + otype, FILENAME_TRANS_MATCH_EXACT, NULL); > if (rc) { > if (rc != SEPOL_EEXIST) > goto err; > @@ -2714,7 +2753,8 @@ out: > return rc; > } > > -static int filename_trans_read_one(policydb_t *p, struct policy_file *fp) > +static int filename_trans_read_one(policydb_t *p, uint32_t match_type, > + struct policy_file *fp) > { > filename_trans_key_t *ft = NULL; > filename_trans_datum_t **dst, *datum, *first = NULL; > @@ -2762,7 +2802,14 @@ static int filename_trans_read_one(policydb_t *p, struct policy_file *fp) > > datum->otype = le32_to_cpu(buf[0]); > > - p->filename_trans_count += ebitmap_cardinality(&datum->stypes); > + /* > + * We need to keep track of the number of exact match filename > + * transitions for writing them in uncompressed format in older > + * binary policy versions. Other match types were not supported > + * back then, so it is not needed > + */ > + if (match_type == FILENAME_TRANS_MATCH_EXACT) > + p->filename_trans_exact_count += ebitmap_cardinality(&datum->stypes); > > dst = &datum->next; > } > @@ -2778,7 +2825,7 @@ static int filename_trans_read_one(policydb_t *p, struct policy_file *fp) > ft->tclass = tclass; > ft->name = name; > > - rc = hashtab_insert(p->filename_trans, (hashtab_key_t)ft, > + rc = hashtab_insert(p->filename_trans[match_type], (hashtab_key_t)ft, > (hashtab_datum_t)first); > if (rc) > goto err; > @@ -2797,7 +2844,8 @@ err: > return -1; > } > > -static int filename_trans_read(policydb_t *p, struct policy_file *fp) > +static int filename_trans_read(policydb_t *p, struct policy_file *fp, > + uint32_t match_type) > { > unsigned int i; > uint32_t buf[1], nel; > @@ -2810,13 +2858,17 @@ static int filename_trans_read(policydb_t *p, struct policy_file *fp) > > if (p->policyvers < POLICYDB_VERSION_COMP_FTRANS) { > for (i = 0; i < nel; i++) { > + /* > + * this version does not have other than exact match > + * transitions > + */ > rc = filename_trans_read_one_compat(p, fp); > if (rc < 0) > return -1; > } > } else { > for (i = 0; i < nel; i++) { > - rc = filename_trans_read_one(p, fp); > + rc = filename_trans_read_one(p, match_type, fp); > if (rc < 0) > return -1; > } > @@ -3743,7 +3795,7 @@ static int role_allow_rule_read(role_allow_rule_t ** r, struct policy_file *fp) > static int filename_trans_rule_read(policydb_t *p, filename_trans_rule_t **r, > struct policy_file *fp) > { > - uint32_t buf[3], nel, i, len; > + uint32_t buf[4], nel, i, len; > unsigned int entries; > filename_trans_rule_t *ftr, *lftr; > int rc; > @@ -3782,7 +3834,9 @@ static int filename_trans_rule_read(policydb_t *p, filename_trans_rule_t **r, > if (type_set_read(&ftr->ttypes, fp)) > return -1; > > - if (p->policyvers >= MOD_POLICYDB_VERSION_SELF_TYPETRANS) > + if (p->policyvers >= MOD_POLICYDB_VERSION_PREFIX_SUFFIX) > + entries = 4; > + else if (p->policyvers >= MOD_POLICYDB_VERSION_SELF_TYPETRANS) > entries = 3; > else > entries = 2; > @@ -3794,6 +3848,8 @@ static int filename_trans_rule_read(policydb_t *p, filename_trans_rule_t **r, > ftr->otype = le32_to_cpu(buf[1]); > if (p->policyvers >= MOD_POLICYDB_VERSION_SELF_TYPETRANS) > ftr->flags = le32_to_cpu(buf[2]); > + if (p->policyvers >= MOD_POLICYDB_VERSION_PREFIX_SUFFIX) > + ftr->match_type = le32_to_cpu(buf[3]); > } > > return 0; > @@ -4356,7 +4412,11 @@ 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 && > - filename_trans_read(p, fp)) > + filename_trans_read(p, fp, FILENAME_TRANS_MATCH_EXACT)) > + goto bad; > + if (r_policyvers >= POLICYDB_VERSION_PREFIX_SUFFIX && > + (filename_trans_read(p, fp, FILENAME_TRANS_MATCH_PREFIX) || > + filename_trans_read(p, fp, FILENAME_TRANS_MATCH_SUFFIX))) > goto bad; > } else { > /* first read the AV rule blocks, then the scope tables */ > diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c > index 8b87675f..d94f49aa 100644 > --- a/libsepol/src/policydb_validate.c > +++ b/libsepol/src/policydb_validate.c > @@ -1147,12 +1147,23 @@ static int validate_filename_trans_hashtab(sepol_handle_t *handle, const policyd > { > map_arg_t margs = { flavors, handle, p }; > > - if (hashtab_map(p->filename_trans, validate_filename_trans, &margs)) { > - ERR(handle, "Invalid filename trans"); > - return -1; > + if (hashtab_map(p->filename_trans[FILENAME_TRANS_MATCH_EXACT], > + validate_filename_trans, &margs)) > + goto bad; > + > + if (p->policyvers >= POLICYDB_VERSION_PREFIX_SUFFIX) { > + if (hashtab_map(p->filename_trans[FILENAME_TRANS_MATCH_PREFIX], > + validate_filename_trans, &margs)) > + goto bad; > + if (hashtab_map(p->filename_trans[FILENAME_TRANS_MATCH_SUFFIX], > + validate_filename_trans, &margs)) > + goto bad; > } > > return 0; > +bad: > + ERR(handle, "Invalid filename trans"); > + return -1; > } > > static int validate_context(const context_struct_t *con, validate_t flavors[], int mls) > @@ -1374,6 +1385,15 @@ static int validate_filename_trans_rules(sepol_handle_t *handle, const filename_ > if (validate_simpletype(filename_trans->otype, p, flavors)) > goto bad; > > + switch (filename_trans->match_type) { > + case FILENAME_TRANS_MATCH_EXACT: > + case FILENAME_TRANS_MATCH_PREFIX: > + case FILENAME_TRANS_MATCH_SUFFIX: > + break; > + default: > + goto bad; > + } > + > /* currently only the RULE_SELF flag can be set */ > if ((filename_trans->flags & ~RULE_SELF) != 0) > goto bad; > diff --git a/libsepol/src/write.c b/libsepol/src/write.c > index 283d11c8..6bcd7535 100644 > --- a/libsepol/src/write.c > +++ b/libsepol/src/write.c > @@ -653,7 +653,8 @@ static int filename_write_one(hashtab_key_t key, void *data, void *ptr) > return 0; > } > > -static int filename_trans_write(struct policydb *p, void *fp) > +static int filename_trans_write(struct policydb *p, uint32_t match_type, > + void *fp) > { > size_t items; > uint32_t buf[1]; > @@ -663,20 +664,25 @@ static int filename_trans_write(struct policydb *p, void *fp) > return 0; > > if (p->policyvers < POLICYDB_VERSION_COMP_FTRANS) { > - buf[0] = cpu_to_le32(p->filename_trans_count); > + /* > + * This version does not have other than exact match > + * transitions, there is no need to count other ones. > + */ > + buf[0] = cpu_to_le32(p->filename_trans_exact_count); > items = put_entry(buf, sizeof(uint32_t), 1, fp); > if (items != 1) > return POLICYDB_ERROR; > > - rc = hashtab_map(p->filename_trans, filename_write_one_compat, > - fp); > + rc = hashtab_map(p->filename_trans[match_type], > + filename_write_one_compat, fp); > } else { > - buf[0] = cpu_to_le32(p->filename_trans->nel); > + buf[0] = cpu_to_le32(p->filename_trans[match_type]->nel); > items = put_entry(buf, sizeof(uint32_t), 1, fp); > if (items != 1) > return POLICYDB_ERROR; > > - rc = hashtab_map(p->filename_trans, filename_write_one, fp); > + rc = hashtab_map(p->filename_trans[match_type], > + filename_write_one, fp); > } > return rc; > } > @@ -1944,11 +1950,25 @@ static int filename_trans_rule_write(policydb_t *p, filename_trans_rule_t *t, > { > int nel = 0; > size_t items, entries; > - uint32_t buf[3], len; > + uint32_t buf[4], len; > filename_trans_rule_t *ftr; > + int discarding = 0; > > - for (ftr = t; ftr; ftr = ftr->next) > - nel++; > + if (p->policyvers >= MOD_POLICYDB_VERSION_PREFIX_SUFFIX) { > + for (ftr = t; ftr; ftr = ftr->next) > + nel++; > + } else { > + for (ftr = t; ftr; ftr = ftr->next) { > + if (ftr->match_type == FILENAME_TRANS_MATCH_EXACT) > + nel++; > + else > + discarding = 1; > + } > + if (discarding) { > + WARN(fp->handle, > + "Discarding prefix/suffix filename type transition rules"); > + } > + } > > buf[0] = cpu_to_le32(nel); > items = put_entry(buf, sizeof(uint32_t), 1, fp); > @@ -1956,6 +1976,9 @@ static int filename_trans_rule_write(policydb_t *p, filename_trans_rule_t *t, > return POLICYDB_ERROR; > > for (ftr = t; ftr; ftr = ftr->next) { > + if (p->policyvers < MOD_POLICYDB_VERSION_PREFIX_SUFFIX && > + ftr->match_type != FILENAME_TRANS_MATCH_EXACT) > + continue; > len = strlen(ftr->name); > buf[0] = cpu_to_le32(len); > items = put_entry(buf, sizeof(uint32_t), 1, fp); > @@ -1974,8 +1997,11 @@ static int filename_trans_rule_write(policydb_t *p, filename_trans_rule_t *t, > buf[0] = cpu_to_le32(ftr->tclass); > buf[1] = cpu_to_le32(ftr->otype); > buf[2] = cpu_to_le32(ftr->flags); > + buf[3] = cpu_to_le32(ftr->match_type); > > - if (p->policyvers >= MOD_POLICYDB_VERSION_SELF_TYPETRANS) { > + if (p->policyvers >= MOD_POLICYDB_VERSION_PREFIX_SUFFIX) { > + entries = 4; > + } else if (p->policyvers >= MOD_POLICYDB_VERSION_SELF_TYPETRANS) { > entries = 3; > } else if (!(ftr->flags & RULE_SELF)) { > entries = 2; > @@ -2370,12 +2396,29 @@ 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 (filename_trans_write(p, fp)) > + if (filename_trans_write(p, FILENAME_TRANS_MATCH_EXACT, > + fp)) > return POLICYDB_ERROR; > } else { > - if (p->filename_trans) > + if (p->filename_trans[FILENAME_TRANS_MATCH_EXACT]) > WARN(fp->handle, "Discarding filename type transition rules"); > } > + if (p->policyvers >= POLICYDB_VERSION_PREFIX_SUFFIX) { > + if (filename_trans_write(p, FILENAME_TRANS_MATCH_PREFIX, > + fp) || > + filename_trans_write(p, FILENAME_TRANS_MATCH_SUFFIX, > + fp)) > + return POLICYDB_ERROR; > + } else { > + if (p->filename_trans[FILENAME_TRANS_MATCH_PREFIX] && > + p->filename_trans[FILENAME_TRANS_MATCH_PREFIX]->nel) > + WARN(fp->handle, > + "Discarding prefix filename type transition rules"); > + if (p->filename_trans[FILENAME_TRANS_MATCH_SUFFIX] && > + p->filename_trans[FILENAME_TRANS_MATCH_SUFFIX]->nel) > + WARN(fp->handle, > + "Discarding suffix filename type transition rules"); > + } > } else { > if (avrule_block_write(p->global, num_syms, p, fp) == -1) { > return POLICYDB_ERROR; > diff --git a/secilc/test/policy.cil b/secilc/test/policy.cil > index e6b78618..73e678a7 100644 > --- a/secilc/test/policy.cil > +++ b/secilc/test/policy.cil > @@ -134,6 +134,8 @@ > (typetransition device_t exec_type files console_device_t) > (typetransition exec_type self files console_device_t) > (typetransition exec_type self files "filename" console_device_t) > + (typetransition exec_type self files "filename" prefix console_device_t) > + (typetransition exec_type self files "filename" suffix console_device_t) > (typechange console_device_t device_t file user_tty_device_t) > (typechange exec_type device_t file user_tty_device_t) > (typechange exec_type self file console_device_t) > @@ -153,6 +155,8 @@ > (rangetransition device_t kernel_t file ((s0) (s3 (not c3)))) > > (typetransition device_t console_t file "some_file" getty_t) > + (typetransition device_t console_t file "some_file" prefix getty_t) > + (typetransition device_t console_t file "some_file" suffix getty_t) > > (allow foo_type self (file (execute))) > (allow bin_t device_t (file (execute))) > -- > 2.42.0 > >
diff --git a/checkpolicy/policy_define.c b/checkpolicy/policy_define.c index 260e609d..fb8325ee 100644 --- a/checkpolicy/policy_define.c +++ b/checkpolicy/policy_define.c @@ -3159,7 +3159,7 @@ avrule_t *define_cond_filename_trans(void) return COND_ERR; } -int define_filename_trans(void) +int define_filename_trans(uint32_t match_type) { char *id, *name = NULL; type_set_t stypes, ttypes; @@ -3261,7 +3261,7 @@ int define_filename_trans(void) ebitmap_for_each_positive_bit(&e_ttypes, tnode, t) { rc = policydb_filetrans_insert( policydbp, s+1, t+1, c+1, name, - NULL, otype, NULL + NULL, otype, match_type, NULL ); if (rc != SEPOL_OK) { if (rc == SEPOL_EEXIST) { @@ -3279,7 +3279,7 @@ int define_filename_trans(void) if (self) { rc = policydb_filetrans_insert( policydbp, s+1, s+1, c+1, name, - NULL, otype, NULL + NULL, otype, match_type, NULL ); if (rc != SEPOL_OK) { if (rc == SEPOL_EEXIST) { @@ -3317,6 +3317,7 @@ int define_filename_trans(void) ftr->tclass = c + 1; ftr->otype = otype; ftr->flags = self ? RULE_SELF : 0; + ftr->match_type = match_type; } free(name); diff --git a/checkpolicy/policy_define.h b/checkpolicy/policy_define.h index 075b048d..05869346 100644 --- a/checkpolicy/policy_define.h +++ b/checkpolicy/policy_define.h @@ -57,7 +57,7 @@ int define_role_trans(int class_specified); int define_role_types(void); int define_role_attr(void); int define_roleattribute(void); -int define_filename_trans(void); +int define_filename_trans(uint32_t match_type); int define_sens(void); int define_te_avtab(int which); int define_te_avtab_extended_perms(int which); diff --git a/checkpolicy/policy_parse.y b/checkpolicy/policy_parse.y index 356626e2..ee4be4ea 100644 --- a/checkpolicy/policy_parse.y +++ b/checkpolicy/policy_parse.y @@ -153,6 +153,7 @@ typedef int (* require_func_t)(int pass); %token FILESYSTEM %token DEFAULT_USER DEFAULT_ROLE DEFAULT_TYPE DEFAULT_RANGE %token LOW_HIGH LOW HIGH GLBLUB +%token PREFIX SUFFIX %left OR %left XOR @@ -410,6 +411,12 @@ cond_rule_def : cond_transition_def { $$ = NULL; } ; cond_transition_def : TYPE_TRANSITION names names ':' names identifier filename ';' + { $$ = define_cond_filename_trans() ; + if ($$ == COND_ERR) return -1;} + | TYPE_TRANSITION names names ':' names identifier filename PREFIX ';' + { $$ = define_cond_filename_trans() ; + if ($$ == COND_ERR) return -1;} + | TYPE_TRANSITION names names ':' names identifier filename SUFFIX ';' { $$ = define_cond_filename_trans() ; if ($$ == COND_ERR) return -1;} | TYPE_TRANSITION names names ':' names identifier ';' @@ -449,7 +456,11 @@ cond_dontaudit_def : DONTAUDIT names names ':' names names ';' ; ; transition_def : TYPE_TRANSITION names names ':' names identifier filename ';' - {if (define_filename_trans()) return -1; } + {if (define_filename_trans(FILENAME_TRANS_MATCH_EXACT)) return -1; } + | TYPE_TRANSITION names names ':' names identifier filename PREFIX ';' + {if (define_filename_trans(FILENAME_TRANS_MATCH_PREFIX)) return -1; } + | TYPE_TRANSITION names names ':' names identifier filename SUFFIX ';' + {if (define_filename_trans(FILENAME_TRANS_MATCH_SUFFIX)) return -1; } | TYPE_TRANSITION names names ':' names identifier ';' {if (define_compute_type(AVRULE_TRANSITION)) return -1;} | TYPE_MEMBER names names ':' names identifier ';' diff --git a/checkpolicy/policy_scan.l b/checkpolicy/policy_scan.l index c998ff8b..0780ef15 100644 --- a/checkpolicy/policy_scan.l +++ b/checkpolicy/policy_scan.l @@ -270,6 +270,10 @@ low | LOW { return(LOW); } glblub | GLBLUB { return(GLBLUB); } +PREFIX | +prefix { return(PREFIX); } +SUFFIX | +suffix { return(SUFFIX); } "/"[^ \n\r\t\f]* { return(PATH); } \""/"[^\"\n]*\" { return(QPATH); } \"[^"/"\"\n]+\" { return(FILENAME); } diff --git a/checkpolicy/test/dismod.c b/checkpolicy/test/dismod.c index 9f4a669b..931af9da 100644 --- a/checkpolicy/test/dismod.c +++ b/checkpolicy/test/dismod.c @@ -571,13 +571,25 @@ static void display_role_allow(role_allow_rule_t * ra, policydb_t * p, FILE * fp static void display_filename_trans(filename_trans_rule_t * tr, policydb_t * p, FILE * fp) { + const char *match_str = ""; fprintf(fp, "filename transition"); for (; tr; tr = tr->next) { display_type_set(&tr->stypes, 0, p, fp); display_type_set(&tr->ttypes, 0, p, fp); display_id(p, fp, SYM_CLASSES, tr->tclass - 1, ":"); display_id(p, fp, SYM_TYPES, tr->otype - 1, ""); - fprintf(fp, " %s\n", tr->name); + switch (tr->match_type) { + case FILENAME_TRANS_MATCH_EXACT: + match_str = ""; + break; + case FILENAME_TRANS_MATCH_PREFIX: + match_str = " prefix"; + break; + case FILENAME_TRANS_MATCH_SUFFIX: + match_str = " suffix"; + break; + } + fprintf(fp, " %s%s\n", tr->name, match_str); } } diff --git a/checkpolicy/test/dispol.c b/checkpolicy/test/dispol.c index 944ef7ec..999c26ca 100644 --- a/checkpolicy/test/dispol.c +++ b/checkpolicy/test/dispol.c @@ -458,6 +458,7 @@ static void display_role_trans(policydb_t *p, FILE *fp) struct filenametr_display_args { policydb_t *p; + const char *match_str; FILE *fp; }; @@ -472,6 +473,7 @@ static int filenametr_display(hashtab_key_t key, FILE *fp = args->fp; ebitmap_node_t *node; uint32_t bit; + const char *match_str = args->match_str; do { ebitmap_for_each_positive_bit(&ftdatum->stypes, node, bit) { @@ -479,7 +481,7 @@ static int filenametr_display(hashtab_key_t key, display_id(p, fp, SYM_TYPES, ft->ttype - 1, ""); display_id(p, fp, SYM_CLASSES, ft->tclass - 1, ":"); display_id(p, fp, SYM_TYPES, ftdatum->otype - 1, ""); - fprintf(fp, " %s\n", ft->name); + fprintf(fp, " %s%s\n", ft->name, match_str); } ftdatum = ftdatum->next; } while (ftdatum); @@ -495,7 +497,15 @@ static void display_filename_trans(policydb_t *p, FILE *fp) fprintf(fp, "filename_trans rules:\n"); args.p = p; args.fp = fp; - hashtab_map(p->filename_trans, filenametr_display, &args); + args.match_str = ""; + hashtab_map(p->filename_trans[FILENAME_TRANS_MATCH_EXACT], + filenametr_display, &args); + args.match_str = " prefix"; + hashtab_map(p->filename_trans[FILENAME_TRANS_MATCH_PREFIX], + filenametr_display, &args); + args.match_str = " suffix"; + hashtab_map(p->filename_trans[FILENAME_TRANS_MATCH_SUFFIX], + filenametr_display, &args); } static int menu(void) diff --git a/checkpolicy/tests/policy_allonce.conf b/checkpolicy/tests/policy_allonce.conf index 34e6402d..fb0a0172 100644 --- a/checkpolicy/tests/policy_allonce.conf +++ b/checkpolicy/tests/policy_allonce.conf @@ -30,6 +30,8 @@ tunable TUNABLE1 false; tunable TUNABLE2 true; type_transition TYPE1 TYPE2 : CLASS1 TYPE3; type_transition { TYPE1 TYPE2 } { TYPE3 TYPE4 } : CLASS1 TYPE1 "FILENAME"; +type_transition { TYPE1 TYPE2 } { TYPE3 TYPE4 } : CLASS1 TYPE1 "FILENAME" prefix; +type_transition { TYPE1 TYPE2 } { TYPE3 TYPE4 } : CLASS1 TYPE1 "FILENAME" suffix; type_member TYPE1 TYPE2 : CLASS1 TYPE2; type_change TYPE1 TYPE2 : CLASS1 TYPE3; allow TYPE1 self : CLASS1 { PERM1 }; diff --git a/checkpolicy/tests/policy_allonce.expected.conf b/checkpolicy/tests/policy_allonce.expected.conf index 63739e1f..e3db7593 100644 --- a/checkpolicy/tests/policy_allonce.expected.conf +++ b/checkpolicy/tests/policy_allonce.expected.conf @@ -40,9 +40,17 @@ dontauditxperm TYPE1 TYPE2:CLASS1 ioctl { 0x3 }; type_transition TYPE1 TYPE2:CLASS1 TYPE3; type_member TYPE1 TYPE2:CLASS1 TYPE2; type_change TYPE1 TYPE2:CLASS1 TYPE3; +type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME" prefix; +type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME" suffix; type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME"; +type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME" prefix; +type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME" suffix; type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME"; +type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME" prefix; +type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME" suffix; type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME"; +type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME" prefix; +type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME" suffix; type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME"; if (BOOL1) { } else { diff --git a/checkpolicy/tests/policy_allonce.expected_opt.conf b/checkpolicy/tests/policy_allonce.expected_opt.conf index 1c969961..94eaf37a 100644 --- a/checkpolicy/tests/policy_allonce.expected_opt.conf +++ b/checkpolicy/tests/policy_allonce.expected_opt.conf @@ -40,9 +40,17 @@ dontauditxperm TYPE1 TYPE2:CLASS1 ioctl { 0x3 }; type_transition TYPE1 TYPE2:CLASS1 TYPE3; type_member TYPE1 TYPE2:CLASS1 TYPE2; type_change TYPE1 TYPE2:CLASS1 TYPE3; +type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME" prefix; +type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME" suffix; type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME"; +type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME" prefix; +type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME" suffix; type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME"; +type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME" prefix; +type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME" suffix; type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME"; +type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME" prefix; +type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME" suffix; type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME"; if (BOOL1) { } else { diff --git a/checkpolicy/tests/policy_allonce_mls.conf b/checkpolicy/tests/policy_allonce_mls.conf index c88616b3..2ba88a00 100644 --- a/checkpolicy/tests/policy_allonce_mls.conf +++ b/checkpolicy/tests/policy_allonce_mls.conf @@ -41,6 +41,8 @@ tunable TUNABLE1 false; tunable TUNABLE2 true; type_transition TYPE1 TYPE2 : CLASS1 TYPE3; type_transition { TYPE1 TYPE2 } { TYPE3 TYPE4 } : CLASS1 TYPE1 "FILENAME"; +type_transition { TYPE1 TYPE2 } { TYPE3 TYPE4 } : CLASS1 TYPE1 "FILENAME" prefix; +type_transition { TYPE1 TYPE2 } { TYPE3 TYPE4 } : CLASS1 TYPE1 "FILENAME" suffix; type_member TYPE1 TYPE2 : CLASS1 TYPE2; type_change TYPE1 TYPE2 : CLASS1 TYPE3; range_transition TYPE1 TYPE2 : CLASS1 s1:c0.c1; diff --git a/checkpolicy/tests/policy_allonce_mls.expected.conf b/checkpolicy/tests/policy_allonce_mls.expected.conf index 87c36f92..d390aac0 100644 --- a/checkpolicy/tests/policy_allonce_mls.expected.conf +++ b/checkpolicy/tests/policy_allonce_mls.expected.conf @@ -51,9 +51,17 @@ dontauditxperm TYPE1 TYPE2:CLASS1 ioctl { 0x3 }; type_transition TYPE1 TYPE2:CLASS1 TYPE3; type_member TYPE1 TYPE2:CLASS1 TYPE2; type_change TYPE1 TYPE2:CLASS1 TYPE3; +type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME" prefix; +type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME" suffix; type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME"; +type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME" prefix; +type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME" suffix; type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME"; +type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME" prefix; +type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME" suffix; type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME"; +type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME" prefix; +type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME" suffix; type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME"; range_transition TYPE1 TYPE2:CLASS1 s1:c0,c1 - s1:c0,c1; if (BOOL1) { diff --git a/checkpolicy/tests/policy_allonce_mls.expected_opt.conf b/checkpolicy/tests/policy_allonce_mls.expected_opt.conf index 38176166..ef683070 100644 --- a/checkpolicy/tests/policy_allonce_mls.expected_opt.conf +++ b/checkpolicy/tests/policy_allonce_mls.expected_opt.conf @@ -51,9 +51,17 @@ dontauditxperm TYPE1 TYPE2:CLASS1 ioctl { 0x3 }; type_transition TYPE1 TYPE2:CLASS1 TYPE3; type_member TYPE1 TYPE2:CLASS1 TYPE2; type_change TYPE1 TYPE2:CLASS1 TYPE3; +type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME" prefix; +type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME" suffix; type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME"; +type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME" prefix; +type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME" suffix; type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME"; +type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME" prefix; +type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME" suffix; type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME"; +type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME" prefix; +type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME" suffix; type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME"; range_transition TYPE1 TYPE2:CLASS1 s1:c0,c1 - s1:c0,c1; if (BOOL1) { diff --git a/libsepol/cil/src/cil.c b/libsepol/cil/src/cil.c index 067e28a6..fd1f7bd8 100644 --- a/libsepol/cil/src/cil.c +++ b/libsepol/cil/src/cil.c @@ -97,6 +97,8 @@ char *CIL_KEY_TUNABLEIF; char *CIL_KEY_ALLOW; char *CIL_KEY_DONTAUDIT; char *CIL_KEY_TYPETRANSITION; +char *CIL_KEY_PREFIX; +char *CIL_KEY_SUFFIX; char *CIL_KEY_TYPECHANGE; char *CIL_KEY_CALL; char *CIL_KEY_TUNABLE; @@ -269,6 +271,8 @@ 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_PREFIX = cil_strpool_add("prefix"); + CIL_KEY_SUFFIX = cil_strpool_add("suffix"); CIL_KEY_TYPECHANGE = cil_strpool_add("typechange"); CIL_KEY_CALL = cil_strpool_add("call"); CIL_KEY_TUNABLE = cil_strpool_add("tunable"); @@ -2461,6 +2465,7 @@ void cil_nametypetransition_init(struct cil_nametypetransition **nametypetrans) (*nametypetrans)->obj = NULL; (*nametypetrans)->name_str = NULL; (*nametypetrans)->name = NULL; + (*nametypetrans)->match_type = FILENAME_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 a8e3616a..75a9e064 100644 --- a/libsepol/cil/src/cil_binary.c +++ b/libsepol/cil/src/cil_binary.c @@ -1168,7 +1168,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, uint32_t match_type, type_datum_t *sepol_result) { int rc; @@ -1183,7 +1183,7 @@ static int __cil_typetransition_to_avtab_helper(policydb_t *pdb, rc = policydb_filetrans_insert( pdb, sepol_src->s.value, sepol_tgt->s.value, sepol_obj->s.value, name, NULL, - sepol_result->s.value, &otype + sepol_result->s.value, match_type, &otype ); if (rc != SEPOL_OK) { if (rc == SEPOL_EEXIST) { @@ -1252,7 +1252,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->match_type, sepol_result ); if (rc != SEPOL_OK) goto exit; } @@ -1270,7 +1270,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->match_type, 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 be260a31..d91b8d20 100644 --- a/libsepol/cil/src/cil_build_ast.c +++ b/libsepol/cil/src/cil_build_ast.c @@ -3386,10 +3386,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; @@ -3405,12 +3406,22 @@ 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 = parse_current->next->next->next->next->next->data; + } } } @@ -3426,7 +3437,22 @@ int cil_gen_typetransition(struct cil_db *db, struct cil_tree_node *parse_curren nametypetrans->obj_str = s3; nametypetrans->name_str = s4; nametypetrans->name = cil_gen_declared_string(db, s4, ast_node); - nametypetrans->result_str = s5; + if (s6) { + if (s5 == CIL_KEY_PREFIX) { + nametypetrans->match_type = FILENAME_TRANS_MATCH_PREFIX; + } else if (s5 == CIL_KEY_SUFFIX) { + nametypetrans->match_type = FILENAME_TRANS_MATCH_SUFFIX; + } else { + rc = SEPOL_ERR; + goto exit; + } + nametypetrans->result_str = s6; + } else { + nametypetrans->result_str = s5; + } + + ast_node->data = nametypetrans; + ast_node->flavor = CIL_NAMETYPETRANSITION; } else { struct cil_type_rule *rule = NULL; cil_type_rule_init(&rule); diff --git a/libsepol/cil/src/cil_copy_ast.c b/libsepol/cil/src/cil_copy_ast.c index 1507edb4..64e2cacd 100644 --- a/libsepol/cil/src/cil_copy_ast.c +++ b/libsepol/cil/src/cil_copy_ast.c @@ -723,6 +723,7 @@ int cil_copy_nametypetransition(__attribute__((unused)) struct cil_db *db, void new->obj_str = orig->obj_str; new->name_str = orig->name_str; new->name = orig->name; + new->match_type = orig->match_type; new->result_str = orig->result_str; diff --git a/libsepol/cil/src/cil_internal.h b/libsepol/cil/src/cil_internal.h index 47b67c89..8c9f1151 100644 --- a/libsepol/cil/src/cil_internal.h +++ b/libsepol/cil/src/cil_internal.h @@ -114,6 +114,8 @@ extern char *CIL_KEY_TUNABLEIF; extern char *CIL_KEY_ALLOW; extern char *CIL_KEY_DONTAUDIT; extern char *CIL_KEY_TYPETRANSITION; +extern char *CIL_KEY_PREFIX; +extern char *CIL_KEY_SUFFIX; extern char *CIL_KEY_TYPECHANGE; extern char *CIL_KEY_CALL; extern char *CIL_KEY_TUNABLE; @@ -588,6 +590,7 @@ struct cil_nametypetransition { struct cil_class *obj; char *name_str; struct cil_symtab_datum *name; + uint32_t match_type; 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 e9a8f75d..b7f096b7 100644 --- a/libsepol/cil/src/cil_policy.c +++ b/libsepol/cil/src/cil_policy.c @@ -1259,15 +1259,30 @@ static void cil_nametypetransition_to_policy(FILE *out, struct cil_nametypetrans struct cil_symtab_datum *src, *tgt, *name, *res; struct cil_list *class_list; struct cil_list_item *i1; + const char *match_type_str = ""; src = trans->src; tgt = trans->tgt; name = trans->name; res = trans->result; + switch (trans->match_type) { + case FILENAME_TRANS_MATCH_EXACT: + match_type_str = ""; + break; + case FILENAME_TRANS_MATCH_PREFIX: + match_type_str = " prefix"; + break; + case FILENAME_TRANS_MATCH_SUFFIX: + match_type_str = " suffix"; + break; + default: + match_type_str = " ???"; + break; + } 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->fqn); + fprintf(out, "type_transition %s %s : %s %s \"%s\"%s;\n", src->fqn, tgt->fqn, DATUM(i1->data)->fqn, res->fqn, name->fqn, match_type_str); } cil_list_destroy(&class_list, CIL_FALSE); } diff --git a/libsepol/cil/src/cil_write_ast.c b/libsepol/cil/src/cil_write_ast.c index f4f9f167..dbf01f5a 100644 --- a/libsepol/cil/src/cil_write_ast.c +++ b/libsepol/cil/src/cil_write_ast.c @@ -1217,6 +1217,16 @@ void cil_write_ast_node(FILE *out, struct cil_tree_node *node) } else { fprintf(out, "%s ", rule->name_str); } + switch (rule->match_type) { + case FILENAME_TRANS_MATCH_EXACT: + break; + case FILENAME_TRANS_MATCH_PREFIX: + fprintf(out, "prefix "); + break; + case FILENAME_TRANS_MATCH_SUFFIX: + fprintf(out, "suffix "); + break; + } fprintf(out, "%s", datum_or_str(DATUM(rule->result), rule->result_str)); fprintf(out, ")\n"); break; diff --git a/libsepol/include/sepol/policydb/policydb.h b/libsepol/include/sepol/policydb/policydb.h index 6682069e..5ee0fa16 100644 --- a/libsepol/include/sepol/policydb/policydb.h +++ b/libsepol/include/sepol/policydb/policydb.h @@ -321,6 +321,8 @@ typedef struct filename_trans_rule { uint32_t tclass; char *name; uint32_t otype; /* new type */ + /* name match type, values from enum filename_trans_match_type */ + uint32_t match_type; struct filename_trans_rule *next; } filename_trans_rule_t; @@ -423,6 +425,14 @@ typedef struct genfs { /* OCON_NUM needs to be the largest index in any platform's ocontext array */ #define OCON_NUM 9 +/* filename transitions table array indices */ +enum filename_trans_match_type { + FILENAME_TRANS_MATCH_EXACT, + FILENAME_TRANS_MATCH_PREFIX, + FILENAME_TRANS_MATCH_SUFFIX, + FILENAME_TRANS_MATCH_NUM, +}; + /* section: module information */ /* scope_index_t holds all of the symbols that are in scope in a @@ -593,8 +603,8 @@ typedef struct policydb { hashtab_t range_tr; /* file transitions with the last path component */ - hashtab_t filename_trans; - uint32_t filename_trans_count; + hashtab_t filename_trans[FILENAME_TRANS_MATCH_NUM]; + uint32_t filename_trans_exact_count; ebitmap_t *type_attr_map; @@ -657,7 +667,8 @@ extern int policydb_sort_ocontexts(policydb_t *p); extern int policydb_filetrans_insert(policydb_t *p, uint32_t stype, uint32_t ttype, uint32_t tclass, const char *name, char **name_alloc, - uint32_t otype, uint32_t *present_otype); + uint32_t otype, uint32_t match_type, + uint32_t *present_otype); /* Deprecated */ extern int policydb_context_isvalid(const policydb_t * p, @@ -758,10 +769,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_PREFIX_SUFFIX 34 /* prefix and suffix filename transitions */ /* 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_PREFIX_SUFFIX /* Module versions and specific changes*/ #define MOD_POLICYDB_VERSION_BASE 4 @@ -784,9 +796,10 @@ extern int policydb_set_target_platform(policydb_t *p, int platform); #define MOD_POLICYDB_VERSION_INFINIBAND 19 #define MOD_POLICYDB_VERSION_GLBLUB 20 #define MOD_POLICYDB_VERSION_SELF_TYPETRANS 21 +#define MOD_POLICYDB_VERSION_PREFIX_SUFFIX 22 #define MOD_POLICYDB_VERSION_MIN MOD_POLICYDB_VERSION_BASE -#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_SELF_TYPETRANS +#define MOD_POLICYDB_VERSION_MAX MOD_POLICYDB_VERSION_PREFIX_SUFFIX #define POLICYDB_CONFIG_MLS 1 diff --git a/libsepol/src/expand.c b/libsepol/src/expand.c index e63414b1..93170a14 100644 --- a/libsepol/src/expand.c +++ b/libsepol/src/expand.c @@ -1419,18 +1419,31 @@ static int expand_filename_trans_helper(expand_state_t *state, rc = policydb_filetrans_insert( state->out, s + 1, t + 1, rule->tclass, rule->name, - NULL, mapped_otype, &present_otype + NULL, mapped_otype, rule->match_type, &present_otype ); if (rc == SEPOL_EEXIST) { /* duplicate rule, ignore */ if (present_otype == mapped_otype) return 0; - ERR(state->handle, "Conflicting name-based type_transition %s %s:%s \"%s\": %s vs %s", + const char *match_str = ""; + switch (rule->match_type) { + case FILENAME_TRANS_MATCH_EXACT: + match_str = ""; + break; + case FILENAME_TRANS_MATCH_PREFIX: + match_str = " prefix"; + break; + case FILENAME_TRANS_MATCH_SUFFIX: + match_str = " suffix"; + break; + } + ERR(state->handle, "Conflicting name-based type_transition %s %s:%s \"%s\"%s: %s vs %s", state->out->p_type_val_to_name[s], state->out->p_type_val_to_name[t], state->out->p_class_val_to_name[rule->tclass - 1], rule->name, + match_str, state->out->p_type_val_to_name[present_otype - 1], state->out->p_type_val_to_name[mapped_otype - 1]); return -1; diff --git a/libsepol/src/kernel_to_cil.c b/libsepol/src/kernel_to_cil.c index 8ec79749..5dd6cfdc 100644 --- a/libsepol/src/kernel_to_cil.c +++ b/libsepol/src/kernel_to_cil.c @@ -1882,6 +1882,7 @@ exit: struct map_filename_trans_args { struct policydb *pdb; + const char *match_str; struct strs *strs; }; @@ -1896,6 +1897,7 @@ static int map_filename_trans_to_str(hashtab_key_t key, void *data, void *arg) struct ebitmap_node *node; uint32_t bit; int rc; + const char *match_str = map_args->match_str; tgt = pdb->p_type_val_to_name[ft->ttype - 1]; class = pdb->p_class_val_to_name[ft->tclass - 1]; @@ -1906,8 +1908,8 @@ static int map_filename_trans_to_str(hashtab_key_t key, void *data, void *arg) ebitmap_for_each_positive_bit(&datum->stypes, node, bit) { src = pdb->p_type_val_to_name[bit]; rc = strs_create_and_add(strs, - "(typetransition %s %s %s \"%s\" %s)", - 5, src, tgt, class, filename, new); + "(typetransition %s %s %s \"%s\"%s %s)", + 6, src, tgt, class, filename, match_str, new); if (rc) return rc; } @@ -1932,7 +1934,23 @@ static int write_filename_trans_rules_to_cil(FILE *out, struct policydb *pdb) args.pdb = pdb; args.strs = strs; - rc = hashtab_map(pdb->filename_trans, map_filename_trans_to_str, &args); + args.match_str = ""; + rc = hashtab_map(pdb->filename_trans[FILENAME_TRANS_MATCH_EXACT], + map_filename_trans_to_str, &args); + if (rc != 0) { + goto exit; + } + + args.match_str = " prefix"; + rc = hashtab_map(pdb->filename_trans[FILENAME_TRANS_MATCH_PREFIX], + map_filename_trans_to_str, &args); + if (rc != 0) { + goto exit; + } + + args.match_str = " suffix"; + rc = hashtab_map(pdb->filename_trans[FILENAME_TRANS_MATCH_SUFFIX], + map_filename_trans_to_str, &args); if (rc != 0) { goto exit; } diff --git a/libsepol/src/kernel_to_conf.c b/libsepol/src/kernel_to_conf.c index b5b530d6..d3631d8d 100644 --- a/libsepol/src/kernel_to_conf.c +++ b/libsepol/src/kernel_to_conf.c @@ -1846,6 +1846,7 @@ exit: struct map_filename_trans_args { struct policydb *pdb; + const char *match_str; struct strs *strs; }; @@ -1860,6 +1861,7 @@ static int map_filename_trans_to_str(hashtab_key_t key, void *data, void *arg) struct ebitmap_node *node; uint32_t bit; int rc; + const char *match_str = map_args->match_str; tgt = pdb->p_type_val_to_name[ft->ttype - 1]; class = pdb->p_class_val_to_name[ft->tclass - 1]; @@ -1870,8 +1872,8 @@ static int map_filename_trans_to_str(hashtab_key_t key, void *data, void *arg) ebitmap_for_each_positive_bit(&datum->stypes, node, bit) { src = pdb->p_type_val_to_name[bit]; rc = strs_create_and_add(strs, - "type_transition %s %s:%s %s \"%s\";", - 5, src, tgt, class, new, filename); + "type_transition %s %s:%s %s \"%s\"%s;", + 6, src, tgt, class, new, filename, match_str); if (rc) return rc; } @@ -1896,7 +1898,23 @@ static int write_filename_trans_rules_to_conf(FILE *out, struct policydb *pdb) args.pdb = pdb; args.strs = strs; - rc = hashtab_map(pdb->filename_trans, map_filename_trans_to_str, &args); + args.match_str = ""; + rc = hashtab_map(pdb->filename_trans[FILENAME_TRANS_MATCH_EXACT], + map_filename_trans_to_str, &args); + if (rc != 0) { + goto exit; + } + + args.match_str = " prefix"; + rc = hashtab_map(pdb->filename_trans[FILENAME_TRANS_MATCH_PREFIX], + map_filename_trans_to_str, &args); + if (rc != 0) { + goto exit; + } + + args.match_str = " suffix"; + rc = hashtab_map(pdb->filename_trans[FILENAME_TRANS_MATCH_SUFFIX], + map_filename_trans_to_str, &args); if (rc != 0) { goto exit; } diff --git a/libsepol/src/link.c b/libsepol/src/link.c index 3b7742bc..f432087f 100644 --- a/libsepol/src/link.c +++ b/libsepol/src/link.c @@ -1440,6 +1440,7 @@ static int copy_filename_trans_list(filename_trans_rule_t * list, new_rule->name = strdup(cur->name); if (!new_rule->name) goto err; + new_rule->match_type = cur->match_type; if (type_set_or_convert(&cur->stypes, &new_rule->stypes, module) || type_set_or_convert(&cur->ttypes, &new_rule->ttypes, module)) diff --git a/libsepol/src/module_to_cil.c b/libsepol/src/module_to_cil.c index ee22dbbd..a90ee85d 100644 --- a/libsepol/src/module_to_cil.c +++ b/libsepol/src/module_to_cil.c @@ -1611,6 +1611,7 @@ static int filename_trans_to_cil(int indent, struct policydb *pdb, struct filena unsigned int ttype; struct type_set *ts; struct filename_trans_rule *rule; + const char *match_str = ""; for (rule = rules; rule != NULL; rule = rule->next) { ts = &rule->stypes; @@ -1625,19 +1626,31 @@ static int filename_trans_to_cil(int indent, struct policydb *pdb, struct filena goto exit; } + switch (rule->match_type) { + case FILENAME_TRANS_MATCH_EXACT: + match_str = ""; + break; + case FILENAME_TRANS_MATCH_PREFIX: + match_str = " prefix"; + break; + case FILENAME_TRANS_MATCH_SUFFIX: + match_str = " suffix"; + break; + } + for (stype = 0; stype < num_stypes; stype++) { for (ttype = 0; ttype < num_ttypes; ttype++) { - cil_println(indent, "(typetransition %s %s %s \"%s\" %s)", + cil_println(indent, "(typetransition %s %s %s \"%s\"%s %s)", stypes[stype], ttypes[ttype], pdb->p_class_val_to_name[rule->tclass - 1], - rule->name, + rule->name, match_str, pdb->p_type_val_to_name[rule->otype - 1]); } if (rule->flags & RULE_SELF) { - cil_println(indent, "(typetransition %s self %s \"%s\" %s)", + cil_println(indent, "(typetransition %s self %s \"%s\"%s %s)", stypes[stype], pdb->p_class_val_to_name[rule->tclass - 1], - rule->name, + rule->name, match_str, pdb->p_type_val_to_name[rule->otype - 1]); } } diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c index 6ba4f916..a1796844 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_PREFIX_SUFFIX, + .sym_num = SYM_NUM, + .ocon_num = OCON_IBENDPORT + 1, + .target_platform = SEPOL_TARGET_SELINUX, + }, { .type = POLICY_BASE, .version = MOD_POLICYDB_VERSION_BASE, @@ -334,6 +341,13 @@ static const struct policydb_compat_info policydb_compat[] = { .ocon_num = OCON_IBENDPORT + 1, .target_platform = SEPOL_TARGET_SELINUX, }, + { + .type = POLICY_BASE, + .version = MOD_POLICYDB_VERSION_PREFIX_SUFFIX, + .sym_num = SYM_NUM, + .ocon_num = OCON_IBENDPORT + 1, + .target_platform = SEPOL_TARGET_SELINUX, + }, { .type = POLICY_MOD, .version = MOD_POLICYDB_VERSION_BASE, @@ -460,6 +474,13 @@ static const struct policydb_compat_info policydb_compat[] = { .ocon_num = 0, .target_platform = SEPOL_TARGET_SELINUX, }, + { + .type = POLICY_MOD, + .version = MOD_POLICYDB_VERSION_PREFIX_SUFFIX, + .sym_num = SYM_NUM, + .ocon_num = 0, + .target_platform = SEPOL_TARGET_SELINUX, + }, }; #if 0 @@ -909,10 +930,14 @@ int policydb_init(policydb_t * p) if (rc) goto err; - p->filename_trans = hashtab_create(filenametr_hash, filenametr_cmp, (1 << 10)); - if (!p->filename_trans) { - rc = -ENOMEM; - goto err; + for (i = 0; i < FILENAME_TRANS_MATCH_NUM; i++) { + p->filename_trans[i] = hashtab_create(filenametr_hash, + filenametr_cmp, + (1 << 10)); + if (!p->filename_trans[i]) { + rc = -ENOMEM; + goto err; + } } p->range_tr = hashtab_create(rangetr_hash, rangetr_cmp, 256); @@ -926,7 +951,9 @@ int policydb_init(policydb_t * p) return 0; err: - hashtab_destroy(p->filename_trans); + for (i = 0; i < FILENAME_TRANS_MATCH_NUM; i++) { + hashtab_destroy(p->filename_trans[i]); + } hashtab_destroy(p->range_tr); for (i = 0; i < SYM_NUM; i++) { hashtab_destroy(p->symtab[i].table); @@ -1564,8 +1591,10 @@ void policydb_destroy(policydb_t * p) if (lra) free(lra); - hashtab_map(p->filename_trans, filenametr_destroy, NULL); - hashtab_destroy(p->filename_trans); + for (i = 0; i < FILENAME_TRANS_MATCH_NUM; i++) { + hashtab_map(p->filename_trans[i], filenametr_destroy, NULL); + hashtab_destroy(p->filename_trans[i]); + } hashtab_map(p->range_tr, range_tr_destroy, NULL); hashtab_destroy(p->range_tr); @@ -2566,7 +2595,7 @@ static int role_allow_read(role_allow_t ** r, struct policy_file *fp) int policydb_filetrans_insert(policydb_t *p, uint32_t stype, uint32_t ttype, uint32_t tclass, const char *name, char **name_alloc, uint32_t otype, - uint32_t *present_otype) + uint32_t match_type, uint32_t *present_otype) { filename_trans_key_t *ft, key; filename_trans_datum_t *datum, *last; @@ -2576,7 +2605,8 @@ int policydb_filetrans_insert(policydb_t *p, uint32_t stype, uint32_t ttype, key.name = (char *)name; last = NULL; - datum = hashtab_search(p->filename_trans, (hashtab_key_t)&key); + datum = hashtab_search(p->filename_trans[match_type], + (hashtab_key_t)&key); while (datum) { if (ebitmap_get_bit(&datum->stypes, stype - 1)) { if (present_otype) @@ -2624,7 +2654,8 @@ int policydb_filetrans_insert(policydb_t *p, uint32_t stype, uint32_t ttype, ft->tclass = tclass; ft->name = name_dup; - if (hashtab_insert(p->filename_trans, (hashtab_key_t)ft, + if (hashtab_insert(p->filename_trans[match_type], + (hashtab_key_t)ft, (hashtab_datum_t)datum)) { free(name_dup); free(datum); @@ -2634,7 +2665,14 @@ int policydb_filetrans_insert(policydb_t *p, uint32_t stype, uint32_t ttype, } } - p->filename_trans_count++; + /* + * We need to keep track of the number of exact match filename + * transitions for writing them in uncompressed format in older binary + * policy versions. Other match types were not supported back then, so + * it is not needed. + */ + if (match_type == FILENAME_TRANS_MATCH_EXACT) + p->filename_trans_exact_count++; return ebitmap_set_bit(&datum->stypes, stype - 1, 1); } @@ -2665,8 +2703,9 @@ static int filename_trans_read_one_compat(policydb_t *p, struct policy_file *fp) tclass = le32_to_cpu(buf[2]); otype = le32_to_cpu(buf[3]); + // This version does not contain other than exact filename transitions rc = policydb_filetrans_insert(p, stype, ttype, tclass, name, &name, - otype, NULL); + otype, FILENAME_TRANS_MATCH_EXACT, NULL); if (rc) { if (rc != SEPOL_EEXIST) goto err; @@ -2714,7 +2753,8 @@ out: return rc; } -static int filename_trans_read_one(policydb_t *p, struct policy_file *fp) +static int filename_trans_read_one(policydb_t *p, uint32_t match_type, + struct policy_file *fp) { filename_trans_key_t *ft = NULL; filename_trans_datum_t **dst, *datum, *first = NULL; @@ -2762,7 +2802,14 @@ static int filename_trans_read_one(policydb_t *p, struct policy_file *fp) datum->otype = le32_to_cpu(buf[0]); - p->filename_trans_count += ebitmap_cardinality(&datum->stypes); + /* + * We need to keep track of the number of exact match filename + * transitions for writing them in uncompressed format in older + * binary policy versions. Other match types were not supported + * back then, so it is not needed + */ + if (match_type == FILENAME_TRANS_MATCH_EXACT) + p->filename_trans_exact_count += ebitmap_cardinality(&datum->stypes); dst = &datum->next; } @@ -2778,7 +2825,7 @@ static int filename_trans_read_one(policydb_t *p, struct policy_file *fp) ft->tclass = tclass; ft->name = name; - rc = hashtab_insert(p->filename_trans, (hashtab_key_t)ft, + rc = hashtab_insert(p->filename_trans[match_type], (hashtab_key_t)ft, (hashtab_datum_t)first); if (rc) goto err; @@ -2797,7 +2844,8 @@ err: return -1; } -static int filename_trans_read(policydb_t *p, struct policy_file *fp) +static int filename_trans_read(policydb_t *p, struct policy_file *fp, + uint32_t match_type) { unsigned int i; uint32_t buf[1], nel; @@ -2810,13 +2858,17 @@ static int filename_trans_read(policydb_t *p, struct policy_file *fp) if (p->policyvers < POLICYDB_VERSION_COMP_FTRANS) { for (i = 0; i < nel; i++) { + /* + * this version does not have other than exact match + * transitions + */ rc = filename_trans_read_one_compat(p, fp); if (rc < 0) return -1; } } else { for (i = 0; i < nel; i++) { - rc = filename_trans_read_one(p, fp); + rc = filename_trans_read_one(p, match_type, fp); if (rc < 0) return -1; } @@ -3743,7 +3795,7 @@ static int role_allow_rule_read(role_allow_rule_t ** r, struct policy_file *fp) static int filename_trans_rule_read(policydb_t *p, filename_trans_rule_t **r, struct policy_file *fp) { - uint32_t buf[3], nel, i, len; + uint32_t buf[4], nel, i, len; unsigned int entries; filename_trans_rule_t *ftr, *lftr; int rc; @@ -3782,7 +3834,9 @@ static int filename_trans_rule_read(policydb_t *p, filename_trans_rule_t **r, if (type_set_read(&ftr->ttypes, fp)) return -1; - if (p->policyvers >= MOD_POLICYDB_VERSION_SELF_TYPETRANS) + if (p->policyvers >= MOD_POLICYDB_VERSION_PREFIX_SUFFIX) + entries = 4; + else if (p->policyvers >= MOD_POLICYDB_VERSION_SELF_TYPETRANS) entries = 3; else entries = 2; @@ -3794,6 +3848,8 @@ static int filename_trans_rule_read(policydb_t *p, filename_trans_rule_t **r, ftr->otype = le32_to_cpu(buf[1]); if (p->policyvers >= MOD_POLICYDB_VERSION_SELF_TYPETRANS) ftr->flags = le32_to_cpu(buf[2]); + if (p->policyvers >= MOD_POLICYDB_VERSION_PREFIX_SUFFIX) + ftr->match_type = le32_to_cpu(buf[3]); } return 0; @@ -4356,7 +4412,11 @@ 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 && - filename_trans_read(p, fp)) + filename_trans_read(p, fp, FILENAME_TRANS_MATCH_EXACT)) + goto bad; + if (r_policyvers >= POLICYDB_VERSION_PREFIX_SUFFIX && + (filename_trans_read(p, fp, FILENAME_TRANS_MATCH_PREFIX) || + filename_trans_read(p, fp, FILENAME_TRANS_MATCH_SUFFIX))) goto bad; } else { /* first read the AV rule blocks, then the scope tables */ diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c index 8b87675f..d94f49aa 100644 --- a/libsepol/src/policydb_validate.c +++ b/libsepol/src/policydb_validate.c @@ -1147,12 +1147,23 @@ static int validate_filename_trans_hashtab(sepol_handle_t *handle, const policyd { map_arg_t margs = { flavors, handle, p }; - if (hashtab_map(p->filename_trans, validate_filename_trans, &margs)) { - ERR(handle, "Invalid filename trans"); - return -1; + if (hashtab_map(p->filename_trans[FILENAME_TRANS_MATCH_EXACT], + validate_filename_trans, &margs)) + goto bad; + + if (p->policyvers >= POLICYDB_VERSION_PREFIX_SUFFIX) { + if (hashtab_map(p->filename_trans[FILENAME_TRANS_MATCH_PREFIX], + validate_filename_trans, &margs)) + goto bad; + if (hashtab_map(p->filename_trans[FILENAME_TRANS_MATCH_SUFFIX], + validate_filename_trans, &margs)) + goto bad; } return 0; +bad: + ERR(handle, "Invalid filename trans"); + return -1; } static int validate_context(const context_struct_t *con, validate_t flavors[], int mls) @@ -1374,6 +1385,15 @@ static int validate_filename_trans_rules(sepol_handle_t *handle, const filename_ if (validate_simpletype(filename_trans->otype, p, flavors)) goto bad; + switch (filename_trans->match_type) { + case FILENAME_TRANS_MATCH_EXACT: + case FILENAME_TRANS_MATCH_PREFIX: + case FILENAME_TRANS_MATCH_SUFFIX: + break; + default: + goto bad; + } + /* currently only the RULE_SELF flag can be set */ if ((filename_trans->flags & ~RULE_SELF) != 0) goto bad; diff --git a/libsepol/src/write.c b/libsepol/src/write.c index 283d11c8..6bcd7535 100644 --- a/libsepol/src/write.c +++ b/libsepol/src/write.c @@ -653,7 +653,8 @@ static int filename_write_one(hashtab_key_t key, void *data, void *ptr) return 0; } -static int filename_trans_write(struct policydb *p, void *fp) +static int filename_trans_write(struct policydb *p, uint32_t match_type, + void *fp) { size_t items; uint32_t buf[1]; @@ -663,20 +664,25 @@ static int filename_trans_write(struct policydb *p, void *fp) return 0; if (p->policyvers < POLICYDB_VERSION_COMP_FTRANS) { - buf[0] = cpu_to_le32(p->filename_trans_count); + /* + * This version does not have other than exact match + * transitions, there is no need to count other ones. + */ + buf[0] = cpu_to_le32(p->filename_trans_exact_count); items = put_entry(buf, sizeof(uint32_t), 1, fp); if (items != 1) return POLICYDB_ERROR; - rc = hashtab_map(p->filename_trans, filename_write_one_compat, - fp); + rc = hashtab_map(p->filename_trans[match_type], + filename_write_one_compat, fp); } else { - buf[0] = cpu_to_le32(p->filename_trans->nel); + buf[0] = cpu_to_le32(p->filename_trans[match_type]->nel); items = put_entry(buf, sizeof(uint32_t), 1, fp); if (items != 1) return POLICYDB_ERROR; - rc = hashtab_map(p->filename_trans, filename_write_one, fp); + rc = hashtab_map(p->filename_trans[match_type], + filename_write_one, fp); } return rc; } @@ -1944,11 +1950,25 @@ static int filename_trans_rule_write(policydb_t *p, filename_trans_rule_t *t, { int nel = 0; size_t items, entries; - uint32_t buf[3], len; + uint32_t buf[4], len; filename_trans_rule_t *ftr; + int discarding = 0; - for (ftr = t; ftr; ftr = ftr->next) - nel++; + if (p->policyvers >= MOD_POLICYDB_VERSION_PREFIX_SUFFIX) { + for (ftr = t; ftr; ftr = ftr->next) + nel++; + } else { + for (ftr = t; ftr; ftr = ftr->next) { + if (ftr->match_type == FILENAME_TRANS_MATCH_EXACT) + nel++; + else + discarding = 1; + } + if (discarding) { + WARN(fp->handle, + "Discarding prefix/suffix filename type transition rules"); + } + } buf[0] = cpu_to_le32(nel); items = put_entry(buf, sizeof(uint32_t), 1, fp); @@ -1956,6 +1976,9 @@ static int filename_trans_rule_write(policydb_t *p, filename_trans_rule_t *t, return POLICYDB_ERROR; for (ftr = t; ftr; ftr = ftr->next) { + if (p->policyvers < MOD_POLICYDB_VERSION_PREFIX_SUFFIX && + ftr->match_type != FILENAME_TRANS_MATCH_EXACT) + continue; len = strlen(ftr->name); buf[0] = cpu_to_le32(len); items = put_entry(buf, sizeof(uint32_t), 1, fp); @@ -1974,8 +1997,11 @@ static int filename_trans_rule_write(policydb_t *p, filename_trans_rule_t *t, buf[0] = cpu_to_le32(ftr->tclass); buf[1] = cpu_to_le32(ftr->otype); buf[2] = cpu_to_le32(ftr->flags); + buf[3] = cpu_to_le32(ftr->match_type); - if (p->policyvers >= MOD_POLICYDB_VERSION_SELF_TYPETRANS) { + if (p->policyvers >= MOD_POLICYDB_VERSION_PREFIX_SUFFIX) { + entries = 4; + } else if (p->policyvers >= MOD_POLICYDB_VERSION_SELF_TYPETRANS) { entries = 3; } else if (!(ftr->flags & RULE_SELF)) { entries = 2; @@ -2370,12 +2396,29 @@ 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 (filename_trans_write(p, fp)) + if (filename_trans_write(p, FILENAME_TRANS_MATCH_EXACT, + fp)) return POLICYDB_ERROR; } else { - if (p->filename_trans) + if (p->filename_trans[FILENAME_TRANS_MATCH_EXACT]) WARN(fp->handle, "Discarding filename type transition rules"); } + if (p->policyvers >= POLICYDB_VERSION_PREFIX_SUFFIX) { + if (filename_trans_write(p, FILENAME_TRANS_MATCH_PREFIX, + fp) || + filename_trans_write(p, FILENAME_TRANS_MATCH_SUFFIX, + fp)) + return POLICYDB_ERROR; + } else { + if (p->filename_trans[FILENAME_TRANS_MATCH_PREFIX] && + p->filename_trans[FILENAME_TRANS_MATCH_PREFIX]->nel) + WARN(fp->handle, + "Discarding prefix filename type transition rules"); + if (p->filename_trans[FILENAME_TRANS_MATCH_SUFFIX] && + p->filename_trans[FILENAME_TRANS_MATCH_SUFFIX]->nel) + WARN(fp->handle, + "Discarding suffix filename type transition rules"); + } } else { if (avrule_block_write(p->global, num_syms, p, fp) == -1) { return POLICYDB_ERROR; diff --git a/secilc/test/policy.cil b/secilc/test/policy.cil index e6b78618..73e678a7 100644 --- a/secilc/test/policy.cil +++ b/secilc/test/policy.cil @@ -134,6 +134,8 @@ (typetransition device_t exec_type files console_device_t) (typetransition exec_type self files console_device_t) (typetransition exec_type self files "filename" console_device_t) + (typetransition exec_type self files "filename" prefix console_device_t) + (typetransition exec_type self files "filename" suffix console_device_t) (typechange console_device_t device_t file user_tty_device_t) (typechange exec_type device_t file user_tty_device_t) (typechange exec_type self file console_device_t) @@ -153,6 +155,8 @@ (rangetransition device_t kernel_t file ((s0) (s3 (not c3)))) (typetransition device_t console_t file "some_file" getty_t) + (typetransition device_t console_t file "some_file" prefix getty_t) + (typetransition device_t console_t file "some_file" suffix getty_t) (allow foo_type self (file (execute))) (allow bin_t device_t (file (execute)))