diff --git a/.gitignore b/.gitignore index 00c1bdae..af8c3119 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ tests/check_startup tests/check_te_checks tests/check_ordering tests/check_perm_macro +tests/check_name_list tests/functional/policies/parse_errors/test3_tmp.if tests/functional/policies/parse_errors/test5_tmp.te tests/functional/policies/parse_errors/test6_tmp.if diff --git a/src/Makefile.am b/src/Makefile.am index e6e5c77a..66451279 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -13,7 +13,7 @@ # limitations under the License. bin_PROGRAMS = selint -selint_SOURCES = main.c lex.l parse.y tree.c tree.h selint_error.h parse_functions.c parse_functions.h maps.c maps.h runner.c runner.h parse_fc.c parse_fc.h template.c template.h file_list.c file_list.h check_hooks.c check_hooks.h fc_checks.c fc_checks.h util.c util.h if_checks.c if_checks.h selint_config.c selint_config.h string_list.c string_list.h startup.c startup.h te_checks.c te_checks.h ordering.c ordering.h color.c color.h perm_macro.c perm_macro.h xalloc.h +selint_SOURCES = main.c lex.l parse.y tree.c tree.h selint_error.h parse_functions.c parse_functions.h maps.c maps.h runner.c runner.h parse_fc.c parse_fc.h template.c template.h file_list.c file_list.h check_hooks.c check_hooks.h fc_checks.c fc_checks.h util.c util.h if_checks.c if_checks.h selint_config.c selint_config.h string_list.c string_list.h startup.c startup.h te_checks.c te_checks.h ordering.c ordering.h color.c color.h perm_macro.c perm_macro.h xalloc.h name_list.c name_list.h BUILT_SOURCES = parse.h AM_YFLAGS = -d -Wno-yacc -Werror=conflicts-rr -Werror=conflicts-sr diff --git a/src/if_checks.c b/src/if_checks.c index 414ec65f..8bd7b98f 100644 --- a/src/if_checks.c +++ b/src/if_checks.c @@ -262,7 +262,7 @@ struct check_result *check_name_used_but_not_required_in_if(const struct const struct policy_node *cur = node; - struct string_list *names_in_current_node = get_names_in_node(node); + struct name_list *names_in_current_node = get_names_in_node(node); if (!names_in_current_node) { return NULL; @@ -276,15 +276,15 @@ struct check_result *check_name_used_but_not_required_in_if(const struct } if (!cur) { - free_string_list(names_in_current_node); + free_name_list(names_in_current_node); return NULL; } // In a template or interface, and cur is a pointer to the definition node cur = cur->first_child; - struct string_list *names_required = NULL; - struct string_list *names_required_tail = NULL; + struct name_list *names_required = NULL; + struct name_list *names_required_tail = NULL; while (cur && cur != node) { if (cur->flavor == NODE_GEN_REQ @@ -305,7 +305,7 @@ struct check_result *check_name_used_but_not_required_in_if(const struct // for example in an ifdef } - const struct string_list *name_node = names_in_current_node; + const struct name_list *name_node = names_in_current_node; /* In declarations skip the first name, which is the new declared type */ if (node->flavor == NODE_DECL) { name_node = name_node->next; @@ -313,27 +313,22 @@ struct check_result *check_name_used_but_not_required_in_if(const struct const char *flavor = NULL; while (name_node) { - if (!str_in_sl(name_node->string, names_required)) { - if (0 == strcmp(name_node->string, "system_r")) { + const struct name_data *ndata = name_node->data; + if (!name_list_contains_name(names_required, ndata)) { + if (name_is_role(ndata) && 0 == strcmp(ndata->name, "system_r")) { // system_r is required by default in all modules // so that is an exception that shouldn't be warned // about. name_node = name_node->next; continue; } - if (look_up_in_decl_map(name_node->string, DECL_TYPE)) { + if (name_is_type(ndata) && look_up_in_decl_map(ndata->name, DECL_TYPE)) { flavor = "Type"; - } else - if (look_up_in_decl_map - (name_node->string, DECL_ATTRIBUTE)) { + } else if (name_is_typeattr(ndata) && look_up_in_decl_map(ndata->name, DECL_ATTRIBUTE)) { flavor = "Attribute"; - } else - if (look_up_in_decl_map - (name_node->string, DECL_ATTRIBUTE_ROLE)) { + } else if (name_is_roleattr(ndata) && look_up_in_decl_map(ndata->name, DECL_ATTRIBUTE_ROLE)) { flavor = "Role Attribute"; - } else - if (look_up_in_decl_map - (name_node->string, DECL_ROLE)) { + } else if (name_is_role(ndata) && look_up_in_decl_map(ndata->name, DECL_ROLE)) { flavor = "Role"; } else { // This is a string we don't recognize. Other checks and/or @@ -344,16 +339,16 @@ struct check_result *check_name_used_but_not_required_in_if(const struct struct check_result *res = make_check_result('W', W_ID_NO_REQ, NOT_REQ_MESSAGE, - flavor, name_node->string); - free_string_list(names_in_current_node); - free_string_list(names_required); + flavor, ndata->name); + free_name_list(names_in_current_node); + free_name_list(names_required); return res; } name_node = name_node->next; } - free_string_list(names_in_current_node); - free_string_list(names_required); + free_name_list(names_in_current_node); + free_name_list(names_required); return NULL; } @@ -405,7 +400,7 @@ struct check_result *check_name_required_but_not_used_in_if(const struct return NULL; } - struct string_list *names_to_check = get_names_in_node(node); + struct name_list *names_to_check = get_names_in_node(node); if (!names_to_check) { // This should never happen return alloc_internal_error( @@ -416,22 +411,22 @@ struct check_result *check_name_required_but_not_used_in_if(const struct cur = cur->next; - struct string_list *sl_end = NULL; - struct string_list *sl_head = NULL; + struct name_list *nl_end = NULL; + struct name_list *nl_head = NULL; int depth = 0; while (cur) { - struct string_list *names_used = get_names_in_node(cur); + struct name_list *names_used = get_names_in_node(cur); if (names_used) { - if (!sl_head) { - sl_head = sl_end = names_used; + if (!nl_head) { + nl_head = nl_end = names_used; } else { - sl_end->next = names_used; + nl_end->next = names_used; } - while (sl_end->next) { - sl_end = sl_end->next; + while (nl_end->next) { + nl_end = nl_end->next; } } @@ -452,24 +447,24 @@ struct check_result *check_name_required_but_not_used_in_if(const struct } } - const struct string_list *name_node = names_to_check; + const struct name_list *name_node = names_to_check; struct check_result *res = NULL; while (name_node) { - if (!str_in_sl(name_node->string, sl_head)) { + if (!name_list_contains_name(nl_head, name_node->data)) { res = make_check_result('W', W_ID_UNUSED_REQ, "%s %s is listed in require block but not used in interface", flavor, - name_node->string); + name_node->data->name); break; } name_node = name_node->next; } - free_string_list(sl_head); - free_string_list(names_to_check); + free_name_list(nl_head); + free_name_list(names_to_check); return res; } diff --git a/src/name_list.c b/src/name_list.c new file mode 100644 index 00000000..7c80cbf8 --- /dev/null +++ b/src/name_list.c @@ -0,0 +1,222 @@ +/* +* Copyright 2022 The SELint Contributors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "name_list.h" + +#include +#include + +#include "tree.h" +#include "xalloc.h" + +static bool is_compatible(enum name_flavor a, enum name_flavor b) +{ + if (b == NAME_UNKNOWN) { + return true; + } + + switch (a) { + case NAME_TYPE: + return b == NAME_TYPE || b == NAME_TYPE_OR_ATTRIBUTE; + case NAME_TYPEATTRIBUTE: + return b == NAME_TYPEATTRIBUTE || b == NAME_TYPE_OR_ATTRIBUTE; + case NAME_TYPE_OR_ATTRIBUTE: + return b == NAME_TYPE || b == NAME_TYPEATTRIBUTE || b == NAME_TYPE_OR_ATTRIBUTE; + case NAME_ROLE: + return b == NAME_ROLE || b == NAME_ROLE_OR_ATTRIBUTE; + case NAME_ROLEATTRIBUTE: + return b == NAME_ROLEATTRIBUTE || b == NAME_ROLE_OR_ATTRIBUTE; + case NAME_ROLE_OR_ATTRIBUTE: + return b == NAME_ROLE || b == NAME_ROLEATTRIBUTE || b == NAME_ROLE_OR_ATTRIBUTE; + case NAME_CLASS: + return b == NAME_CLASS; + case NAME_PERM: + return b == NAME_PERM; + case NAME_USER: + return b == NAME_USER; + case NAME_BOOL: + return b == NAME_BOOL; + case NAME_UNKNOWN: + return true; + default: + // should never happen + return 0; + } +} + +bool name_is_type(const struct name_data *name) +{ + return is_compatible(name->flavor, NAME_TYPE); +} + +bool name_is_typeattr(const struct name_data *name) +{ + return is_compatible(name->flavor, NAME_TYPEATTRIBUTE); +} + +bool name_is_role(const struct name_data *name) +{ + return is_compatible(name->flavor, NAME_ROLE); +} + +bool name_is_roleattr(const struct name_data *name) +{ + return is_compatible(name->flavor, NAME_ROLEATTRIBUTE); +} + +bool name_is_class(const struct name_data *name) +{ + return is_compatible(name->flavor, NAME_CLASS); +} + +bool name_list_contains_name(const struct name_list *nl, const struct name_data *name) +{ + for (;nl; nl = nl->next) { + if (!is_compatible(nl->data->flavor, name->flavor)) { + continue; + } + + if (0 == strcmp(nl->data->name, name->name)) { + return true; + } + } + return false; +} + +struct name_list *name_list_from_sl_with_traits(const struct string_list *sl, + enum name_flavor flavor, + const struct string_list *traits) +{ + if (!sl) { + return NULL; + } + struct name_list *ret = xmalloc(sizeof(struct name_list)); + struct name_list *cur = ret; + + while (sl) { + struct name_data *data = xmalloc(sizeof(struct name_data)); + data->name = xstrdup(sl->string); + data->flavor = flavor; + data->traits = copy_string_list(traits); + cur->data = data; + + if (sl->next) { + cur->next = xmalloc(sizeof(struct name_list)); + } else { + cur->next = NULL; + } + sl = sl->next; + cur = cur->next; + } + return ret; +} + +struct name_list *name_list_create(const char *name, enum name_flavor flavor) +{ + struct name_data *data = xmalloc(sizeof(struct name_data)); + data->name = xstrdup(name); + data->flavor = flavor; + data->traits = NULL; + struct name_list *ret = xmalloc(sizeof(struct name_list)); + ret->data = data; + ret->next = NULL; + + return ret; +} + +struct name_list *name_list_from_decl(const struct declaration_data *decl) +{ + struct name_data *data = xmalloc(sizeof(struct name_data)); + data->name = xstrdup(decl->name); + data->traits = NULL; + + struct name_list *extra = NULL; + + switch (decl->flavor) { + case DECL_TYPE: + data->flavor = NAME_TYPE; + extra = name_list_from_sl(decl->attrs, NAME_TYPEATTRIBUTE); + break; + case DECL_ATTRIBUTE: + data->flavor = NAME_TYPEATTRIBUTE; + break; + case DECL_ATTRIBUTE_ROLE: + data->flavor = NAME_ROLEATTRIBUTE; + break; + case DECL_ROLE: + data->flavor = NAME_ROLE; + break; + case DECL_USER: + data->flavor = NAME_USER; + break; + case DECL_CLASS: + data->flavor = NAME_CLASS; + data->traits = copy_string_list(decl->attrs); + break; + case DECL_PERM: + data->flavor = NAME_PERM; + break; + case DECL_BOOL: + data->flavor = NAME_BOOL; + break; + default: + // should never happen + data->flavor = NAME_UNKNOWN; + break; + } + + struct name_list *ret = xmalloc(sizeof(struct name_list)); + ret->data = data; + ret->next = extra; + + return ret; +} + +struct name_list *concat_name_lists(struct name_list *head, struct name_list *tail) +{ + if (!head) { + return tail; + } + + if (!tail) { + return head; + } + + struct name_list *cur = head; + while (cur->next) { + cur = cur->next; + } + cur->next = tail; + + return head; +} + +void free_name_list(struct name_list *list) +{ + if (list == NULL) { + return; + } + struct name_list *cur = list; + + while (cur) { + struct name_list *to_free = cur; + cur = cur->next; + free(to_free->data->name); + free_string_list(to_free->data->traits); + free(to_free->data); + free(to_free); + } +} diff --git a/src/name_list.h b/src/name_list.h new file mode 100644 index 00000000..dddad97d --- /dev/null +++ b/src/name_list.h @@ -0,0 +1,83 @@ +/* +* Copyright 2022 The SELint Contributors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef NAME_LIST_H +#define NAME_LIST_H + +#include + +#include "string_list.h" +// avoid circular include with tree.h +struct declaration_data; + + +enum name_flavor { + NAME_UNKNOWN, + NAME_TYPE, + NAME_TYPEATTRIBUTE, + NAME_TYPE_OR_ATTRIBUTE, + NAME_ROLE, + NAME_ROLEATTRIBUTE, + NAME_ROLE_OR_ATTRIBUTE, + NAME_CLASS, + NAME_PERM, + NAME_USER, + NAME_BOOL, +}; + +struct name_data { + enum name_flavor flavor; + char *name; + // flavor == NAME_CLASS: list of associated permissions + struct string_list *traits; +}; + +bool name_is_type(const struct name_data *name); +bool name_is_typeattr(const struct name_data *name); +bool name_is_role(const struct name_data *name); +bool name_is_roleattr(const struct name_data *name); +bool name_is_class(const struct name_data *name); + +struct name_list { + struct name_data *data; + struct name_list *next; +}; + +// Create a name list with a single entry +struct name_list *name_list_create(const char *name, enum name_flavor flavor); + +// Create a name list with identifiers from a string list and associate all with flavor +struct name_list *name_list_from_sl_with_traits(const struct string_list *sl, + enum name_flavor flavor, + const struct string_list *traits); +static inline struct name_list *name_list_from_sl(const struct string_list *sl, + enum name_flavor flavor) +{ + return name_list_from_sl_with_traits(sl, flavor, NULL); +} + +// Concat two name lists, accepts NULL lists. +// Note: freeing the returned list will free both original name lists +struct name_list *concat_name_lists(struct name_list *head, struct name_list *tail); + +// Create a name list with a single entry from a declaration +struct name_list *name_list_from_decl(const struct declaration_data *decl); + +bool name_list_contains_name(const struct name_list *nl, const struct name_data *name); + +void free_name_list(struct name_list *nl); + +#endif diff --git a/src/ordering.c b/src/ordering.c index fc281adc..c9cb48b2 100644 --- a/src/ordering.c +++ b/src/ordering.c @@ -358,21 +358,21 @@ static bool is_own_module_rule(const struct policy_node *node, const char *curre return false; } } - struct string_list *names = get_names_in_node(node); - struct string_list *cur = names; + struct name_list *names = get_names_in_node(node); + const struct name_list *cur = names; while (cur) { - const char *module_of_type_or_attr = look_up_in_decl_map(cur->string, DECL_TYPE); + const char *module_of_type_or_attr = look_up_in_decl_map(cur->data->name, DECL_TYPE); if (!module_of_type_or_attr) { - module_of_type_or_attr = look_up_in_decl_map(cur->string, DECL_ATTRIBUTE); + module_of_type_or_attr = look_up_in_decl_map(cur->data->name, DECL_ATTRIBUTE); } if (module_of_type_or_attr && 0 != strcmp(module_of_type_or_attr, current_mod_name)) { - free_string_list(names); + free_name_list(names); return false; } cur = cur->next; } - free_string_list(names); + free_name_list(names); // This assumes that not found strings are not types from other modules. // This is probably necessary because we'll find strings like "file" or // "read_file_perms" for example. However, in normal mode without context diff --git a/src/te_checks.c b/src/te_checks.c index 9c9f9d98..bbfc7e36 100644 --- a/src/te_checks.c +++ b/src/te_checks.c @@ -512,23 +512,24 @@ struct check_result *check_no_explicit_declaration(const struct check_data *data return NULL; } - struct string_list *names = get_names_in_node(node); + struct name_list *names = get_names_in_node(node); - const struct string_list *name = names; + const struct name_list *name = names; /* In declarations skip the first name, which is the new declared type */ if (node->flavor == NODE_DECL) { name = name->next; } for (; name; name = name->next) { + const struct name_data *ndata = name->data; const char *mod_name; enum decl_flavor flavor; - if ((mod_name = look_up_in_decl_map(name->string, DECL_TYPE))) { + if (name_is_type(ndata) && (mod_name = look_up_in_decl_map(ndata->name, DECL_TYPE))) { flavor = DECL_TYPE; - } else if ((mod_name = look_up_in_decl_map(name->string, DECL_ATTRIBUTE))) { + } else if (name_is_typeattr(ndata) && (mod_name = look_up_in_decl_map(ndata->name, DECL_ATTRIBUTE))) { flavor = DECL_ATTRIBUTE; - } else if ((mod_name = look_up_in_decl_map(name->string, DECL_ATTRIBUTE_ROLE))) { + } else if (name_is_roleattr(ndata) && (mod_name = look_up_in_decl_map(ndata->name, DECL_ATTRIBUTE_ROLE))) { flavor = DECL_ATTRIBUTE_ROLE; // Do not check for roles: in refpolicy they are defined in the kernel module // and used in role modules (like unprivuser) @@ -539,19 +540,19 @@ struct check_result *check_no_explicit_declaration(const struct check_data *data if (0 != strcmp(data->mod_name, mod_name)) { // It may be required - if (!has_require(node, name->string, flavor)) { + if (!has_require(node, ndata->name, flavor)) { // We didn't find a require block with this name struct check_result *to_ret = make_check_result('W', W_ID_NO_EXPLICIT_DECL, "No explicit declaration for %s from module %s. You should access it via interface call or use a require block.", - name->string, mod_name); - free_string_list(names); + ndata->name, mod_name); + free_name_list(names); return to_ret; } // Otherwise, keep checking other names in this node } } - free_string_list(names); + free_name_list(names); return NULL; } diff --git a/src/tree.c b/src/tree.c index a0c7cafa..e074138d 100644 --- a/src/tree.c +++ b/src/tree.c @@ -144,11 +144,11 @@ const char *decl_flavor_to_string(enum decl_flavor flavor) } } -struct string_list *get_names_in_node(const struct policy_node *node) +struct name_list *get_names_in_node(const struct policy_node *node) { - struct string_list *ret = NULL; - struct string_list *cur = NULL; + struct name_list *ret = NULL; + struct name_list *cur = NULL; struct av_rule_data *av_data; struct type_transition_data *tt_data; struct role_transition_data *rt_data; @@ -164,96 +164,65 @@ struct string_list *get_names_in_node(const struct policy_node *node) // Since the common elements are ordered identically, we can just look // at the common subset for the XAV rule av_data = node->data.av_data; - cur = ret = copy_string_list(av_data->sources); - if (cur) { - while (cur->next) { - cur = cur->next; - } - cur->next = copy_string_list(av_data->targets); - } else { - ret = copy_string_list(av_data->targets); - } + ret = name_list_from_sl(av_data->sources, NAME_TYPE_OR_ATTRIBUTE); + ret = concat_name_lists(ret, name_list_from_sl(av_data->targets, NAME_TYPE_OR_ATTRIBUTE)); + ret = concat_name_lists(ret, name_list_from_sl_with_traits(av_data->object_classes, NAME_CLASS, av_data->perms)); break; case NODE_TT_RULE: tt_data = node->data.tt_data; - cur = ret = copy_string_list(tt_data->sources); - if (cur) { - while (cur->next) { - cur = cur->next; - } - cur->next = copy_string_list(tt_data->targets); - } else { - cur = ret = copy_string_list(tt_data->targets); - } - if (cur) { - while (cur->next) { - cur = cur->next; - } - cur->next = sl_from_str(tt_data->default_type); - } else { - ret = sl_from_str(tt_data->default_type); - } + ret = name_list_from_sl(tt_data->sources, NAME_TYPE_OR_ATTRIBUTE); + ret = concat_name_lists(ret, name_list_from_sl(tt_data->targets, NAME_TYPE_OR_ATTRIBUTE)); + ret = concat_name_lists(ret, name_list_create(tt_data->default_type, NAME_TYPE)); + ret = concat_name_lists(ret, name_list_from_sl(tt_data->object_classes, NAME_CLASS)); break; case NODE_RT_RULE: rt_data = node->data.rt_data; - cur = ret = copy_string_list(rt_data->sources); - if (cur) { - while (cur->next) { - cur = cur->next; - } - cur->next = copy_string_list(rt_data->targets); - } else { - cur = ret = copy_string_list(rt_data->targets); - } - if (cur) { - while (cur->next) { - cur = cur->next; - } - cur->next = sl_from_str(rt_data->default_role); - } else { - ret = sl_from_str(rt_data->default_role); - } + ret = name_list_from_sl(rt_data->sources, NAME_ROLE_OR_ATTRIBUTE); + ret = concat_name_lists(ret, name_list_from_sl(rt_data->targets, NAME_TYPE_OR_ATTRIBUTE)); + ret = concat_name_lists(ret, name_list_create(rt_data->default_role, NAME_ROLE)); + ret = concat_name_lists(ret, name_list_from_sl(rt_data->object_classes, NAME_CLASS)); break; case NODE_DECL: d_data = node->data.d_data; - ret = sl_from_str(d_data->name); - ret->next = copy_string_list(d_data->attrs); + ret = name_list_from_decl(d_data); break; case NODE_IF_CALL: ifc_data = node->data.ic_data; - ret = copy_string_list(ifc_data->args); + ret = name_list_from_sl(ifc_data->args, NAME_UNKNOWN); break; case NODE_ROLE_ALLOW: ra_data = node->data.ra_data; - cur = ret = copy_string_list(ra_data->from); - while (cur->next) { - cur = cur->next; - } - cur->next = copy_string_list(ra_data->to); + ret = name_list_from_sl(ra_data->from, NAME_ROLE_OR_ATTRIBUTE); + ret = concat_name_lists(ret, name_list_from_sl(ra_data->to, NAME_ROLE_OR_ATTRIBUTE)); break; case NODE_ROLE_TYPES: rtyp_data = node->data.rtyp_data; - ret = sl_from_str(rtyp_data->role); - ret->next = copy_string_list(rtyp_data->types); + ret = name_list_create(rtyp_data->role, NAME_ROLE_OR_ATTRIBUTE); + ret->next = name_list_from_sl(rtyp_data->types, NAME_TYPE_OR_ATTRIBUTE); break; case NODE_TYPE_ATTRIBUTE: + at_data = node->data.at_data; + ret = name_list_create(at_data->type, NAME_TYPE); + ret->next = name_list_from_sl(at_data->attrs, NAME_TYPEATTRIBUTE); + break; + case NODE_ROLE_ATTRIBUTE: at_data = node->data.at_data; - ret = sl_from_str(at_data->type); - ret->next = copy_string_list(at_data->attrs); + ret = name_list_create(at_data->type, NAME_ROLE); + ret->next = name_list_from_sl(at_data->attrs, NAME_ROLEATTRIBUTE); break; case NODE_ALIAS: case NODE_TYPE_ALIAS: case NODE_PERMISSIVE: - ret = sl_from_str(node->data.str); + ret = name_list_create(node->data.str, NAME_TYPE); break; /* @@ -293,11 +262,12 @@ struct string_list *get_names_in_node(const struct policy_node *node) // Check if any of the types are exclusions cur = ret; while (cur) { - if (cur->string[0] == '-') { + char *name = cur->data->name; + if (name[0] == '-') { // memmove is safe for overlapping strings // Length is strlen exactly because it doesn't copy the first // character, but does copy the null terminator - memmove(cur->string, cur->string + 1, strlen(cur->string)); + memmove(name, name + 1, strlen(name)); } cur = cur->next; } @@ -305,10 +275,10 @@ struct string_list *get_names_in_node(const struct policy_node *node) return ret; } -struct string_list *get_names_required(const struct policy_node *node) +struct name_list *get_names_required(const struct policy_node *node) { - struct string_list *ret = NULL; - struct string_list *ret_cursor = NULL; + struct name_list *ret = NULL; + struct name_list *ret_cursor = NULL; struct policy_node *cur = node->first_child; diff --git a/src/tree.h b/src/tree.h index 86ba3dfc..1ea52658 100644 --- a/src/tree.h +++ b/src/tree.h @@ -17,6 +17,7 @@ #ifndef TREE_H #define TREE_H +#include "name_list.h" #include "selint_error.h" #include "string_list.h" @@ -241,9 +242,9 @@ int is_template_call(const struct policy_node *node); const char *get_name_if_in_template(const struct policy_node *cur); -struct string_list *get_names_in_node(const struct policy_node *node); +struct name_list *get_names_in_node(const struct policy_node *node); -struct string_list *get_names_required(const struct policy_node *node); +struct name_list *get_names_required(const struct policy_node *node); const char *decl_flavor_to_string(enum decl_flavor flavor); diff --git a/tests/Makefile.am b/tests/Makefile.am index 859914d3..d43009ee 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -15,7 +15,7 @@ @VALGRIND_CHECK_RULES@ VALGRIND_memcheck_FLAGS=--leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all -TESTS = check_tree check_parse_functions check_maps check_parsing check_parse_fc check_template check_file_list check_fc_checks check_check_hooks check_selint_config check_if_checks check_string_list check_runner check_startup check_te_checks check_ordering check_perm_macro +TESTS = check_tree check_parse_functions check_maps check_parsing check_parse_fc check_template check_file_list check_fc_checks check_check_hooks check_selint_config check_if_checks check_string_list check_runner check_startup check_te_checks check_ordering check_perm_macro check_name_list check_PROGRAMS = ${TESTS} AV_FILE_PERM_FILES=sample_av/file/index \ @@ -283,14 +283,16 @@ UTIL_OBJS=$(top_builddir)/src/util.o SELINT_ERROR_HEADS=$(top_builddir)/src/selint_error.h STRING_LIST_HEADS=$(top_builddir)/src/string_list.h ${SELINT_ERROR_HEADS} STRING_LIST_OBJS=$(top_builddir)/src/string_list.o +NAME_LIST_HEADS=$(top_builddir)/src/name_list.h +NAME_LIST_OBJS=$(top_builddir)/src/name_list.o ${STRING_LIST_OBJS} COLOR_HEADS=$(top_builddir)/src/color.h COLOR_OBJS=$(top_builddir)/src/color.o PERM_MACRO_HEADS=$(top_builddir)/src/perm_macro.h ${UTIL_HEADS} ${COLOR_HEADS} PERM_MACRO_OBJS=$(top_builddir)/src/perm_macro.o ${UTIL_OBJS} ${COLOR_OBJS} SELINT_CONFIG_HEADS=$(top_builddir)/src/selint_config.h ${STRING_LIST_HEADS} ${TREE_HEADS} ${MAPS_HEADS} ${ORDERING_HEADS} SELINT_CONFIG_OBJS=$(top_builddir)/src/selint_config.o ${STRING_LIST_OBJS} ${TREE_OBJS} ${MAPS_OBJS} ${UTIL_OBJS} -TREE_HEADS=$(top_builddir)/src/tree.h ${STRING_LIST_HEADS} -TREE_OBJS=$(top_builddir)/src/tree.o ${STRING_LIST_OBJS} $(top_builddir)/src/maps.o +TREE_HEADS=$(top_builddir)/src/tree.h ${STRING_LIST_HEADS} ${NAME_LIST_HEADS} +TREE_OBJS=$(top_builddir)/src/tree.o ${NAME_LIST_OBJS} $(top_builddir)/src/maps.o FILE_LIST_HEADS=$(top_builddir)/src/file_list.h ${TREE_HEADS} FILE_LIST_OBJS=$(top_builddir)/src/file_list.o ${TREE_OBJS} MAPS_HEADS=$(top_builddir)/src/maps.h ${SELINT_ERROR_HEADS} ${TREE_HEADS} @@ -321,6 +323,9 @@ ORDERING_OBJS=$(top_builddir)/src/ordering.o ${TREE_OBJS} ${MAPS_OBJS} check_string_list_SOURCES = check_string_list.c ${STRING_LIST_HEADS} check_string_list_LDADD = @CHECK_LIBS@ $(sort ${STRING_LIST_OBJS}) +check_name_list_SOURCES = check_name_list.c ${NAME_LIST_HEADS} +check_name_list_LDADD = @CHECK_LIBS@ $(sort ${NAME_LIST_OBJS}) + check_selint_config_SOURCES = check_selint_config.c ${SELINT_CONFIG_HEADS} ${STRING_LIST_HEADS} check_selint_config_LDADD = @CHECK_LIBS@ $(sort ${SELINT_CONFIG_OBJS} ${STRING_LIST_OBJS}) diff --git a/tests/check_name_list.c b/tests/check_name_list.c new file mode 100644 index 00000000..4e891c72 --- /dev/null +++ b/tests/check_name_list.c @@ -0,0 +1,297 @@ +/* + * Copyright 2022 The SELint Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "../src/name_list.h" +#include "../src/tree.h" + +START_TEST (test_name_list_create) { + + struct name_list *nl = name_list_create("foo", NAME_ROLEATTRIBUTE); + + ck_assert_ptr_nonnull(nl); + ck_assert_ptr_nonnull(nl->data); + ck_assert_str_eq("foo", nl->data->name); + ck_assert_int_eq(NAME_ROLEATTRIBUTE, nl->data->flavor); + ck_assert_ptr_null(nl->data->traits); + ck_assert_ptr_null(nl->next); + + free_name_list(nl); + +} +END_TEST + +START_TEST (test_name_list_from_sl) { + + struct string_list *sl, *traits; + struct name_list *nl; + + sl = NULL; + nl = name_list_from_sl_with_traits(sl, NAME_TYPE, NULL); + ck_assert_ptr_null(nl); + + sl = sl_from_strs(2, "foo", "bar"); + nl = name_list_from_sl_with_traits(sl, NAME_TYPE, NULL); + + ck_assert_ptr_nonnull(nl); + ck_assert_ptr_nonnull(nl->data); + ck_assert_str_eq("foo", nl->data->name); + ck_assert_int_eq(NAME_TYPE, nl->data->flavor); + ck_assert_ptr_null(nl->data->traits); + + ck_assert_ptr_nonnull(nl->next); + ck_assert_str_eq("bar", nl->next->data->name); + ck_assert_int_eq(NAME_TYPE, nl->next->data->flavor); + ck_assert_ptr_null(nl->next->data->traits); + ck_assert_ptr_null(nl->next->next); + + free_name_list(nl); + free_string_list(sl); + + sl = sl_from_strs(3, "foo", "bar", "baz"); + traits = sl_from_strs(2, "alpha", "beta"); + nl = name_list_from_sl_with_traits(sl, NAME_ROLE, traits); + + ck_assert_ptr_nonnull(nl); + ck_assert_ptr_nonnull(nl->data); + ck_assert_str_eq("foo", nl->data->name); + ck_assert_int_eq(NAME_ROLE, nl->data->flavor); + ck_assert_ptr_nonnull(nl->data->traits); + ck_assert_str_eq("alpha", nl->data->traits->string); + ck_assert_str_eq("beta", nl->data->traits->next->string); + ck_assert_ptr_null(nl->data->traits->next->next); + + ck_assert_ptr_nonnull(nl->next); + ck_assert_ptr_nonnull(nl->next->data); + ck_assert_str_eq("bar", nl->next->data->name); + ck_assert_int_eq(NAME_ROLE, nl->next->data->flavor); + ck_assert_ptr_nonnull(nl->next->data->traits); + ck_assert_str_eq("alpha", nl->next->data->traits->string); + ck_assert_str_eq("beta", nl->next->data->traits->next->string); + ck_assert_ptr_null(nl->next->data->traits->next->next); + + ck_assert_ptr_nonnull(nl->next->next); + ck_assert_ptr_nonnull(nl->next->next->data); + ck_assert_str_eq("baz", nl->next->next->data->name); + ck_assert_int_eq(NAME_ROLE, nl->next->next->data->flavor); + ck_assert_ptr_nonnull(nl->next->next->data->traits); + ck_assert_str_eq("alpha", nl->next->next->data->traits->string); + ck_assert_str_eq("beta", nl->next->next->data->traits->next->string); + ck_assert_ptr_null(nl->next->next->data->traits->next->next); + + ck_assert_ptr_null(nl->next->next->next); + + free_name_list(nl); + free_string_list(traits); + free_string_list(sl); + +} +END_TEST + +START_TEST (test_concat_name_lists) { + + struct name_list *res, *nl1, *nl2; + + ck_assert_ptr_null(concat_name_lists(NULL, NULL)); + + nl1 = name_list_create("hello", NAME_TYPEATTRIBUTE); + ck_assert_ptr_nonnull(nl1); + + nl2 = name_list_create("world", NAME_CLASS); + ck_assert_ptr_nonnull(nl2); + + res = concat_name_lists(NULL, nl1); + ck_assert_ptr_nonnull(res); + ck_assert_str_eq("hello", res->data->name); + ck_assert_int_eq(NAME_TYPEATTRIBUTE, res->data->flavor); + ck_assert_ptr_null(res->next); + + res = concat_name_lists(nl1, NULL); + ck_assert_ptr_nonnull(res); + ck_assert_str_eq("hello", res->data->name); + ck_assert_int_eq(NAME_TYPEATTRIBUTE, res->data->flavor); + ck_assert_ptr_null(res->next); + + res = concat_name_lists(nl1, nl2); + ck_assert_ptr_nonnull(res); + ck_assert_str_eq("hello", res->data->name); + ck_assert_int_eq(NAME_TYPEATTRIBUTE, res->data->flavor); + ck_assert_ptr_nonnull(res->next); + ck_assert_str_eq("world", res->next->data->name); + ck_assert_int_eq(NAME_CLASS, res->next->data->flavor); + ck_assert_ptr_null(res->next->next); + + free_name_list(res); // frees nl1 and nl2 + +} +END_TEST + +START_TEST (test_name_lists_from_type_decl) { + + struct declaration_data *decl = malloc(sizeof(struct declaration_data)); + decl->flavor = DECL_TYPE; + decl->name = strdup("foo"); + decl->attrs = sl_from_strs(2, "alpha", "beta"); + + struct name_list *nl = name_list_from_decl(decl); + + free_string_list(decl->attrs); + free(decl->name); + free(decl); + + ck_assert_ptr_nonnull(nl); + ck_assert_ptr_nonnull(nl->data); + ck_assert_str_eq("foo", nl->data->name); + ck_assert_int_eq(NAME_TYPE, nl->data->flavor); + ck_assert_ptr_null(nl->data->traits); + + ck_assert_ptr_nonnull(nl->next); + ck_assert_ptr_nonnull(nl->next->data); + ck_assert_str_eq("alpha", nl->next->data->name); + ck_assert_int_eq(NAME_TYPEATTRIBUTE, nl->next->data->flavor); + ck_assert_ptr_null(nl->next->data->traits); + + ck_assert_ptr_nonnull(nl->next->next); + ck_assert_ptr_nonnull(nl->next->next->data); + ck_assert_str_eq("beta", nl->next->next->data->name); + ck_assert_int_eq(NAME_TYPEATTRIBUTE, nl->next->next->data->flavor); + ck_assert_ptr_null(nl->next->next->data->traits); + + ck_assert_ptr_null(nl->next->next->next); + + free_name_list(nl); + +} +END_TEST + +START_TEST (test_name_lists_from_class_decl) { + + struct declaration_data *decl = malloc(sizeof(struct declaration_data)); + decl->flavor = DECL_CLASS; + decl->name = strdup("foo"); + decl->attrs = sl_from_strs(2, "alpha", "beta"); + + struct name_list *nl = name_list_from_decl(decl); + + free_string_list(decl->attrs); + free(decl->name); + free(decl); + + ck_assert_ptr_nonnull(nl); + ck_assert_ptr_nonnull(nl->data); + ck_assert_str_eq("foo", nl->data->name); + ck_assert_int_eq(NAME_CLASS, nl->data->flavor); + ck_assert_ptr_nonnull(nl->data->traits); + ck_assert_str_eq("alpha", nl->data->traits->string); + ck_assert_str_eq("beta", nl->data->traits->next->string); + ck_assert_ptr_null(nl->data->traits->next->next); + + ck_assert_ptr_null(nl->next); + + free_name_list(nl); + +} +END_TEST + +START_TEST (test_name_list_contains) { + + struct name_list *d; + struct name_list *nl = concat_name_lists( + name_list_create("foo", NAME_TYPEATTRIBUTE), + name_list_create("bar", NAME_ROLE)); + + ck_assert_ptr_nonnull(nl); + + d = name_list_create("foo", NAME_TYPEATTRIBUTE); + ck_assert_int_eq(1, name_list_contains_name(nl, d->data)); + free_name_list(d); + + d = name_list_create("foo", NAME_TYPE_OR_ATTRIBUTE); + ck_assert_int_eq(1, name_list_contains_name(nl, d->data)); + free_name_list(d); + + d = name_list_create("foo", NAME_UNKNOWN); + ck_assert_int_eq(1, name_list_contains_name(nl, d->data)); + free_name_list(d); + + d = name_list_create("foo", NAME_TYPE); + ck_assert_int_eq(0, name_list_contains_name(nl, d->data)); + free_name_list(d); + + d = name_list_create("foo", NAME_BOOL); + ck_assert_int_eq(0, name_list_contains_name(nl, d->data)); + free_name_list(d); + + d = name_list_create("bar", NAME_ROLE); + ck_assert_int_eq(1, name_list_contains_name(nl, d->data)); + free_name_list(d); + + d = name_list_create("bar", NAME_ROLE_OR_ATTRIBUTE); + ck_assert_int_eq(1, name_list_contains_name(nl, d->data)); + free_name_list(d); + + d = name_list_create("bar", NAME_UNKNOWN); + ck_assert_int_eq(1, name_list_contains_name(nl, d->data)); + free_name_list(d); + + d = name_list_create("bar", NAME_ROLEATTRIBUTE); + ck_assert_int_eq(0, name_list_contains_name(nl, d->data)); + free_name_list(d); + + d = name_list_create("bar", NAME_TYPE); + ck_assert_int_eq(0, name_list_contains_name(nl, d->data)); + free_name_list(d); + + free_name_list(nl); + +} +END_TEST + +static Suite *name_list_suite(void) { + Suite *s; + TCase *tc_core; + + s = suite_create("Name_list"); + + tc_core = tcase_create("Core"); + + tcase_add_test(tc_core, test_name_list_create); + tcase_add_test(tc_core, test_name_list_from_sl); + tcase_add_test(tc_core, test_concat_name_lists); + tcase_add_test(tc_core, test_name_lists_from_type_decl); + tcase_add_test(tc_core, test_name_lists_from_class_decl); + tcase_add_test(tc_core, test_name_list_contains); + suite_add_tcase(s, tc_core); + + return s; +} + +int main(void) { + + int number_failed = 0; + Suite *s; + SRunner *sr; + + s = name_list_suite(); + sr = srunner_create(s); + srunner_run_all(sr, CK_NORMAL); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + + return (number_failed == 0)? 0 : -1; +} diff --git a/tests/check_tree.c b/tests/check_tree.c index aaefc03b..2212f7e2 100644 --- a/tests/check_tree.c +++ b/tests/check_tree.c @@ -108,26 +108,35 @@ START_TEST (test_get_types_in_node_av) { node->data.av_data = make_example_av_rule(); - struct string_list *out = get_names_in_node(node); + struct name_list *out = get_names_in_node(node); - struct string_list *cur = out; + struct name_list *cur = out; ck_assert_ptr_nonnull(cur); - ck_assert_str_eq(cur->string, EXAMPLE_TYPE_1); + ck_assert_str_eq(cur->data->name, EXAMPLE_TYPE_1); + ck_assert_int_eq(cur->data->flavor, NAME_TYPE_OR_ATTRIBUTE); cur = cur->next; ck_assert_ptr_nonnull(cur); - ck_assert_str_eq(cur->string, EXAMPLE_TYPE_2); + ck_assert_str_eq(cur->data->name, EXAMPLE_TYPE_2); + ck_assert_int_eq(cur->data->flavor, NAME_TYPE_OR_ATTRIBUTE); cur = cur->next; ck_assert_ptr_nonnull(cur); - ck_assert_str_eq(cur->string, EXAMPLE_TYPE_3); + ck_assert_str_eq(cur->data->name, EXAMPLE_TYPE_3); + ck_assert_int_eq(cur->data->flavor, NAME_TYPE_OR_ATTRIBUTE); + + cur = cur->next; + + ck_assert_ptr_nonnull(cur); + ck_assert_str_eq(cur->data->name, "file"); + ck_assert_int_eq(cur->data->flavor, NAME_CLASS); ck_assert_ptr_null(cur->next); - free_string_list(out); + free_name_list(out); free_policy_node(node); } END_TEST @@ -149,26 +158,29 @@ START_TEST (test_get_types_in_node_tt) { tt_data->default_type = strdup(EXAMPLE_TYPE_1); - struct string_list *out = get_names_in_node(node); + struct name_list *out = get_names_in_node(node); - struct string_list *cur = out; + struct name_list *cur = out; ck_assert_ptr_nonnull(cur); - ck_assert_str_eq(cur->string, EXAMPLE_TYPE_3); + ck_assert_str_eq(cur->data->name, EXAMPLE_TYPE_3); + ck_assert_int_eq(cur->data->flavor, NAME_TYPE_OR_ATTRIBUTE); cur = cur->next; ck_assert_ptr_nonnull(cur); - ck_assert_str_eq(cur->string, EXAMPLE_TYPE_2); + ck_assert_str_eq(cur->data->name, EXAMPLE_TYPE_2); + ck_assert_int_eq(cur->data->flavor, NAME_TYPE_OR_ATTRIBUTE); cur = cur->next; ck_assert_ptr_nonnull(cur); - ck_assert_str_eq(cur->string, EXAMPLE_TYPE_1); + ck_assert_str_eq(cur->data->name, EXAMPLE_TYPE_1); + ck_assert_int_eq(cur->data->flavor, NAME_TYPE); ck_assert_ptr_null(cur->next); - free_string_list(out); + free_name_list(out); free_policy_node(node); } END_TEST @@ -184,15 +196,16 @@ START_TEST (test_get_types_in_node_dd) { d_data->name = strdup(EXAMPLE_TYPE_2); - struct string_list *out = get_names_in_node(node); + struct name_list *out = get_names_in_node(node); ck_assert_ptr_nonnull(out); - ck_assert_str_eq(out->string, EXAMPLE_TYPE_2); + ck_assert_str_eq(out->data->name, EXAMPLE_TYPE_2); + ck_assert_int_eq(out->data->flavor, NAME_TYPE); ck_assert_ptr_null(out->next); - free_string_list(out); + free_name_list(out); free_policy_node(node); } END_TEST @@ -212,16 +225,18 @@ START_TEST (test_get_types_in_node_if_call) { if_data->args->next = calloc(1, sizeof(struct string_list)); if_data->args->next->string = strdup("baz_t"); - struct string_list *out = get_names_in_node(node); + struct name_list *out = get_names_in_node(node); ck_assert_ptr_nonnull(out); - ck_assert_str_eq(out->string, "bar_t"); - ck_assert_str_eq(out->next->string, "baz_t"); + ck_assert_str_eq(out->data->name, "bar_t"); + ck_assert_int_eq(out->data->flavor, NAME_UNKNOWN); + ck_assert_str_eq(out->next->data->name, "baz_t"); + ck_assert_int_eq(out->next->data->flavor, NAME_UNKNOWN); ck_assert_ptr_null(out->next->next); - free_string_list(out); + free_name_list(out); free_policy_node(node); } END_TEST @@ -247,14 +262,16 @@ START_TEST (test_get_types_in_node_exclusion) { node->data.av_data->sources->next = calloc(1, sizeof(struct string_list)); node->data.av_data->sources->next->string = strdup("-init_t"); - struct string_list *out = get_names_in_node(node); + struct name_list *out = get_names_in_node(node); ck_assert_ptr_nonnull(out); - ck_assert_str_eq(out->string, "domain"); - ck_assert_str_eq(out->next->string, "init_t"); // Strip "-" + ck_assert_str_eq(out->data->name, "domain"); + ck_assert_int_eq(out->data->flavor, NAME_TYPE_OR_ATTRIBUTE); + ck_assert_str_eq(out->next->data->name, "init_t"); // Strip "-" + ck_assert_int_eq(out->next->data->flavor, NAME_TYPE_OR_ATTRIBUTE); ck_assert_ptr_null(out->next->next); - free_string_list(out); + free_name_list(out); free_policy_node(node); } END_TEST