Skip to content

Commit

Permalink
Infer parameter types of interfaces and templates
Browse files Browse the repository at this point in the history
Infer the type of parameters of templates and interfaces.
This can help find mis-declarations, see next commit.

In the future this can be used for further checks, e.g. find
discrepancies between the kind of a parameter and its documentation.
  • Loading branch information
cgzones committed May 10, 2021
1 parent 53967a4 commit 1d190c4
Show file tree
Hide file tree
Showing 13 changed files with 927 additions and 21 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ tests/check_startup
tests/check_te_checks
tests/check_ordering
tests/check_perm_macro
tests/check_infer
tests/functional/policies/parse_errors/test3_tmp.if
tests/functional/policies/parse_errors/test5_tmp.te
tests/functional/policies/parse_errors/test6_tmp.if
Expand Down
2 changes: 1 addition & 1 deletion src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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
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 infer.c infer.h
BUILT_SOURCES = parse.h
AM_YFLAGS = -d -Wno-yacc -Werror=conflicts-rr -Werror=conflicts-sr

Expand Down
333 changes: 333 additions & 0 deletions src/infer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,333 @@
/*
* Copyright 2021 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 <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "infer.h"
#include "color.h"
#include "maps.h"
#include "util.h"

/* Uses directly in the testsuite */
enum selint_error infer_interfaces_shallow(const struct policy_node *node);
enum selint_error infer_interfaces_deep(const struct policy_node *node);

static enum param_flavor name_to_param_flavor(enum name_flavor flavor)
{
switch (flavor) {
case NAME_TYPE:
return PARAM_TYPE;
case NAME_TYPEATTRIBUTE:
return PARAM_TYPEATTRIBUTE;
case NAME_TYPE_OR_ATTRIBUTE:
return PARAM_TYPE_OR_ATTRIBUTE;
case NAME_ROLE:
return PARAM_ROLE;
case NAME_ROLEATTRIBUTE:
return PARAM_ROLEATTRIBUTE;
case NAME_ROLE_OR_ATTRIBUTE:
return PARAM_ROLE_OR_ATTRIBUTE;
case NAME_CLASS:
return PARAM_CLASS;
case NAME_OBJECT_NAME:
return PARAM_OBJECT_NAME;
default:
// should never happen
return PARAM_UNKNOWN;
}
}

enum infer_type { IN_SHALLOW, IN_DEEP };

struct infer_data {
struct interface_trait *if_data;
enum infer_type mode;
const struct policy_node *node;
};

static const char *trait_type_to_str(enum trait_type t)
{
switch (t) {
case INTERFACE_TRAIT:
return "interface";
case TEMPLATE_TRAIT:
return "template";
case MACRO_TRAIT:
return "macro";
default:
// should never happen
return "unknown-trait-type";
}
}

static void infer_func(const char *name, enum name_flavor flavor, unsigned short id, void *visitor_data)
{
if (!name) {
return;
}

struct infer_data *data = visitor_data;

const char *dollar = strchr(name, '$');
if (!dollar) {
return;
}

if (0 == strcmp(name, "$*") && flavor == NAME_IF_PARAM && id == 1) {
const struct interface_trait *call_trait = look_up_in_if_traits_map(data->node->data.ic_data->name);
if (!call_trait) {
print_if_verbose("No call trait for %s\n", data->node->data.ic_data->name);
} else {
for (int i = 0; i < TRAIT_MAX_PARAMETERS; i++) {
data->if_data->parameters[i] = call_trait->parameters[i];
}
}
return;
}

char *param_end;
errno = 0;
unsigned long param_no = strtoul(dollar + 1, &param_end, 10);
if (param_no == 0 || errno != 0) {
fprintf(stderr, "%sError%s: Failed to parse parameter number from name '%s' in %s %s!\n",
color_error(), color_reset(),
name,
trait_type_to_str(data->if_data->type),
data->if_data->name);
return;
}
param_no--; // start counting at 0 ($0 is invalid)
if (param_no > TRAIT_MAX_PARAMETERS) {
fprintf(stderr, "%sWarning%s: Only up to %u parameters supported, parsed %lu from name '%s' in %s %s!\n",
color_warning(), color_reset(),
TRAIT_MAX_PARAMETERS,
param_no,
name,
trait_type_to_str(data->if_data->type),
data->if_data->name);
return;
}

// skip dash of exclusions
if (name[0] == '-') {
name = name + 1;
}

if (dollar == name && *param_end == '\0') {
// name is just a parameter, e.g. '$1'
if (data->if_data->parameters[param_no] < PARAM_FINAL_INFERRED) {
if (data->mode == IN_DEEP && flavor == NAME_IF_PARAM) {
const struct interface_trait *call_trait = look_up_in_if_traits_map(data->node->data.ic_data->name);
if (!call_trait) {
print_if_verbose("No call trait for %s\n", data->node->data.ic_data->name);
} else {
data->if_data->parameters[param_no] = call_trait->parameters[id-1];
}
} else {
data->if_data->parameters[param_no] = name_to_param_flavor(flavor);
}
}
return;
}

if (data->if_data->parameters[param_no] < PARAM_FINAL_INFERRED) {
data->if_data->parameters[param_no] = PARAM_TEXT;
}
}

static enum selint_error infer_interface(struct interface_trait *if_trait, const struct policy_node *node, enum infer_type mode)
{
struct infer_data data = { if_trait, mode, NULL };
static unsigned short nesting = 1;

if (nesting > 40) {
return SELINT_IF_CALL_LOOP;
}

for (; node && node->flavor != NODE_INTERFACE_DEF && node->flavor != NODE_TEMP_DEF; node = dfs_next(node)) {
if (mode == IN_DEEP && node->flavor == NODE_IF_CALL) {
const char *call_name = node->data.ic_data->name;
struct interface_trait *call_trait = look_up_in_if_traits_map(call_name);
if (!call_trait) {
print_if_verbose("No call trait found for %s\n", call_name);
} else if (!call_trait->is_inferred && call_trait->type != MACRO_TRAIT) {
nesting++;
enum selint_error ret = infer_interface(call_trait, call_trait->node->first_child, mode);
nesting--;
if (ret != SELINT_SUCCESS) {
return ret;
}
}
}

data.node = node;
visit_names_in_node(node, infer_func, &data);
}

return SELINT_SUCCESS;
}

enum selint_error infer_interfaces_shallow(const struct policy_node *node)
{
for (const struct policy_node *cur_node = node; cur_node; cur_node = cur_node->next) {
// skip non ifs
if (cur_node->flavor != NODE_INTERFACE_DEF && cur_node->flavor != NODE_TEMP_DEF) {
continue;
}

struct interface_trait *if_trait = malloc(sizeof(struct interface_trait));
if_trait->name = strdup(cur_node->data.str);
if_trait->type = (cur_node->flavor == NODE_TEMP_DEF) ? TEMPLATE_TRAIT : INTERFACE_TRAIT;
if_trait->is_inferred = false;
memset(if_trait->parameters, 0, sizeof if_trait->parameters);
if_trait->node = cur_node;

enum selint_error ret = infer_interface(if_trait, cur_node->first_child, IN_SHALLOW);
if (ret != SELINT_SUCCESS) {
return ret;
}

bool is_inferred = true;
for (int i = 0; i < TRAIT_MAX_PARAMETERS; ++i) {
if (if_trait->parameters[i] == PARAM_UNKNOWN) {
is_inferred = false;
break;
}
}
if_trait->is_inferred = is_inferred;

insert_into_if_traits_map(cur_node->data.str, if_trait);
}

return SELINT_SUCCESS;
}

enum selint_error infer_interfaces_deep(const struct policy_node *node)
{
for (const struct policy_node *cur_node = node; cur_node; cur_node = cur_node->next) {
// skip non ifs
if (cur_node->flavor != NODE_INTERFACE_DEF && cur_node->flavor != NODE_TEMP_DEF) {
continue;
}

const char *if_name = cur_node->data.str;
struct interface_trait *if_trait = look_up_in_if_traits_map(if_name);

if (if_trait->is_inferred) {
continue;
}

enum selint_error ret = infer_interface(if_trait, cur_node->first_child, IN_DEEP);
if (ret != SELINT_SUCCESS) {
return ret;
}
for (int i = 0; i < TRAIT_MAX_PARAMETERS; ++i) {
if (if_trait->parameters[i] == PARAM_UNKNOWN) {
print_if_verbose("Parameter %d of %s %s not inferred\n",
i + 1,
trait_type_to_str(if_trait->type),
if_trait->name);
}
}
if_trait->is_inferred = true;
}

return SELINT_SUCCESS;
}

static void add_refpolicy_macro(const char *name, int param_count, const enum param_flavor flavors[])
{
struct interface_trait *if_trait = malloc(sizeof(struct interface_trait));
if_trait->name = strdup(name);
if_trait->type = MACRO_TRAIT;
if_trait->is_inferred = true;
if_trait->node = NULL;
for (int i = 0; i < TRAIT_MAX_PARAMETERS; i++) {
if (i < param_count) {
if_trait->parameters[i] = flavors[i];
} else {
if_trait->parameters[i] = PARAM_INITIAL;
}
}

insert_into_if_traits_map(name, if_trait);
}

enum selint_error infer_all_interfaces(const struct policy_file_list *files)
{
// manually insert common refpolicy macros, since macro definitions are not
// part of the internal policy representation
add_refpolicy_macro("can_exec",
2,
(enum param_flavor[2]){ PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE_OR_ATTRIBUTE });
add_refpolicy_macro("filetrans_pattern",
5,
(enum param_flavor[5]){ PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE, PARAM_CLASS, PARAM_OBJECT_NAME });
add_refpolicy_macro("filetrans_add_pattern",
5,
(enum param_flavor[5]){ PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE, PARAM_CLASS, PARAM_OBJECT_NAME });
add_refpolicy_macro("domtrans_pattern",
3,
(enum param_flavor[3]){ PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE });
add_refpolicy_macro("domain_auto_transition_pattern",
3,
(enum param_flavor[3]){ PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE });
add_refpolicy_macro("admin_pattern",
2,
(enum param_flavor[2]){ PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE_OR_ATTRIBUTE });
add_refpolicy_macro("stream_connect_pattern",
4,
(enum param_flavor[4]){ PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE_OR_ATTRIBUTE });
add_refpolicy_macro("dgram_send_pattern",
4,
(enum param_flavor[4]){ PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE_OR_ATTRIBUTE });


// first infer only simple ifs; do not infer based on other called sub ifs
print_if_verbose("Start shallow infer step...\n");
for (const struct policy_file_node *cur_file = files->head; cur_file; cur_file = cur_file->next) {
enum selint_error ret = infer_interfaces_shallow(cur_file->file->ast);
if (ret != SELINT_SUCCESS) {
return ret;
}
}

// on the second run the policy_nodes are linked to the traits, so we can infer deep
print_if_verbose("Start deep infer step...\n");
for (const struct policy_file_node *cur_file = files->head; cur_file; cur_file = cur_file->next) {
enum selint_error ret = infer_interfaces_deep(cur_file->file->ast);
if (ret != SELINT_SUCCESS) {
return ret;
}
}

print_if_verbose("Finished infer steps\n");

return SELINT_SUCCESS;
}

void free_interface_trait(struct interface_trait *to_free)
{
if (to_free == NULL) {
return;
}

free(to_free->name);
free(to_free);
}
Loading

0 comments on commit 1d190c4

Please sign in to comment.