Skip to content

Commit 1d190c4

Browse files
committed
Infer parameter types of interfaces and templates
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.
1 parent 53967a4 commit 1d190c4

File tree

13 files changed

+927
-21
lines changed

13 files changed

+927
-21
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ tests/check_startup
3232
tests/check_te_checks
3333
tests/check_ordering
3434
tests/check_perm_macro
35+
tests/check_infer
3536
tests/functional/policies/parse_errors/test3_tmp.if
3637
tests/functional/policies/parse_errors/test5_tmp.te
3738
tests/functional/policies/parse_errors/test6_tmp.if

src/Makefile.am

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# limitations under the License.
1414

1515
bin_PROGRAMS = selint
16-
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
16+
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
1717
BUILT_SOURCES = parse.h
1818
AM_YFLAGS = -d -Wno-yacc -Werror=conflicts-rr -Werror=conflicts-sr
1919

src/infer.c

Lines changed: 333 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,333 @@
1+
/*
2+
* Copyright 2021 The SELint Contributors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include <errno.h>
18+
#include <stdlib.h>
19+
#include <stdio.h>
20+
#include <string.h>
21+
22+
#include "infer.h"
23+
#include "color.h"
24+
#include "maps.h"
25+
#include "util.h"
26+
27+
/* Uses directly in the testsuite */
28+
enum selint_error infer_interfaces_shallow(const struct policy_node *node);
29+
enum selint_error infer_interfaces_deep(const struct policy_node *node);
30+
31+
static enum param_flavor name_to_param_flavor(enum name_flavor flavor)
32+
{
33+
switch (flavor) {
34+
case NAME_TYPE:
35+
return PARAM_TYPE;
36+
case NAME_TYPEATTRIBUTE:
37+
return PARAM_TYPEATTRIBUTE;
38+
case NAME_TYPE_OR_ATTRIBUTE:
39+
return PARAM_TYPE_OR_ATTRIBUTE;
40+
case NAME_ROLE:
41+
return PARAM_ROLE;
42+
case NAME_ROLEATTRIBUTE:
43+
return PARAM_ROLEATTRIBUTE;
44+
case NAME_ROLE_OR_ATTRIBUTE:
45+
return PARAM_ROLE_OR_ATTRIBUTE;
46+
case NAME_CLASS:
47+
return PARAM_CLASS;
48+
case NAME_OBJECT_NAME:
49+
return PARAM_OBJECT_NAME;
50+
default:
51+
// should never happen
52+
return PARAM_UNKNOWN;
53+
}
54+
}
55+
56+
enum infer_type { IN_SHALLOW, IN_DEEP };
57+
58+
struct infer_data {
59+
struct interface_trait *if_data;
60+
enum infer_type mode;
61+
const struct policy_node *node;
62+
};
63+
64+
static const char *trait_type_to_str(enum trait_type t)
65+
{
66+
switch (t) {
67+
case INTERFACE_TRAIT:
68+
return "interface";
69+
case TEMPLATE_TRAIT:
70+
return "template";
71+
case MACRO_TRAIT:
72+
return "macro";
73+
default:
74+
// should never happen
75+
return "unknown-trait-type";
76+
}
77+
}
78+
79+
static void infer_func(const char *name, enum name_flavor flavor, unsigned short id, void *visitor_data)
80+
{
81+
if (!name) {
82+
return;
83+
}
84+
85+
struct infer_data *data = visitor_data;
86+
87+
const char *dollar = strchr(name, '$');
88+
if (!dollar) {
89+
return;
90+
}
91+
92+
if (0 == strcmp(name, "$*") && flavor == NAME_IF_PARAM && id == 1) {
93+
const struct interface_trait *call_trait = look_up_in_if_traits_map(data->node->data.ic_data->name);
94+
if (!call_trait) {
95+
print_if_verbose("No call trait for %s\n", data->node->data.ic_data->name);
96+
} else {
97+
for (int i = 0; i < TRAIT_MAX_PARAMETERS; i++) {
98+
data->if_data->parameters[i] = call_trait->parameters[i];
99+
}
100+
}
101+
return;
102+
}
103+
104+
char *param_end;
105+
errno = 0;
106+
unsigned long param_no = strtoul(dollar + 1, &param_end, 10);
107+
if (param_no == 0 || errno != 0) {
108+
fprintf(stderr, "%sError%s: Failed to parse parameter number from name '%s' in %s %s!\n",
109+
color_error(), color_reset(),
110+
name,
111+
trait_type_to_str(data->if_data->type),
112+
data->if_data->name);
113+
return;
114+
}
115+
param_no--; // start counting at 0 ($0 is invalid)
116+
if (param_no > TRAIT_MAX_PARAMETERS) {
117+
fprintf(stderr, "%sWarning%s: Only up to %u parameters supported, parsed %lu from name '%s' in %s %s!\n",
118+
color_warning(), color_reset(),
119+
TRAIT_MAX_PARAMETERS,
120+
param_no,
121+
name,
122+
trait_type_to_str(data->if_data->type),
123+
data->if_data->name);
124+
return;
125+
}
126+
127+
// skip dash of exclusions
128+
if (name[0] == '-') {
129+
name = name + 1;
130+
}
131+
132+
if (dollar == name && *param_end == '\0') {
133+
// name is just a parameter, e.g. '$1'
134+
if (data->if_data->parameters[param_no] < PARAM_FINAL_INFERRED) {
135+
if (data->mode == IN_DEEP && flavor == NAME_IF_PARAM) {
136+
const struct interface_trait *call_trait = look_up_in_if_traits_map(data->node->data.ic_data->name);
137+
if (!call_trait) {
138+
print_if_verbose("No call trait for %s\n", data->node->data.ic_data->name);
139+
} else {
140+
data->if_data->parameters[param_no] = call_trait->parameters[id-1];
141+
}
142+
} else {
143+
data->if_data->parameters[param_no] = name_to_param_flavor(flavor);
144+
}
145+
}
146+
return;
147+
}
148+
149+
if (data->if_data->parameters[param_no] < PARAM_FINAL_INFERRED) {
150+
data->if_data->parameters[param_no] = PARAM_TEXT;
151+
}
152+
}
153+
154+
static enum selint_error infer_interface(struct interface_trait *if_trait, const struct policy_node *node, enum infer_type mode)
155+
{
156+
struct infer_data data = { if_trait, mode, NULL };
157+
static unsigned short nesting = 1;
158+
159+
if (nesting > 40) {
160+
return SELINT_IF_CALL_LOOP;
161+
}
162+
163+
for (; node && node->flavor != NODE_INTERFACE_DEF && node->flavor != NODE_TEMP_DEF; node = dfs_next(node)) {
164+
if (mode == IN_DEEP && node->flavor == NODE_IF_CALL) {
165+
const char *call_name = node->data.ic_data->name;
166+
struct interface_trait *call_trait = look_up_in_if_traits_map(call_name);
167+
if (!call_trait) {
168+
print_if_verbose("No call trait found for %s\n", call_name);
169+
} else if (!call_trait->is_inferred && call_trait->type != MACRO_TRAIT) {
170+
nesting++;
171+
enum selint_error ret = infer_interface(call_trait, call_trait->node->first_child, mode);
172+
nesting--;
173+
if (ret != SELINT_SUCCESS) {
174+
return ret;
175+
}
176+
}
177+
}
178+
179+
data.node = node;
180+
visit_names_in_node(node, infer_func, &data);
181+
}
182+
183+
return SELINT_SUCCESS;
184+
}
185+
186+
enum selint_error infer_interfaces_shallow(const struct policy_node *node)
187+
{
188+
for (const struct policy_node *cur_node = node; cur_node; cur_node = cur_node->next) {
189+
// skip non ifs
190+
if (cur_node->flavor != NODE_INTERFACE_DEF && cur_node->flavor != NODE_TEMP_DEF) {
191+
continue;
192+
}
193+
194+
struct interface_trait *if_trait = malloc(sizeof(struct interface_trait));
195+
if_trait->name = strdup(cur_node->data.str);
196+
if_trait->type = (cur_node->flavor == NODE_TEMP_DEF) ? TEMPLATE_TRAIT : INTERFACE_TRAIT;
197+
if_trait->is_inferred = false;
198+
memset(if_trait->parameters, 0, sizeof if_trait->parameters);
199+
if_trait->node = cur_node;
200+
201+
enum selint_error ret = infer_interface(if_trait, cur_node->first_child, IN_SHALLOW);
202+
if (ret != SELINT_SUCCESS) {
203+
return ret;
204+
}
205+
206+
bool is_inferred = true;
207+
for (int i = 0; i < TRAIT_MAX_PARAMETERS; ++i) {
208+
if (if_trait->parameters[i] == PARAM_UNKNOWN) {
209+
is_inferred = false;
210+
break;
211+
}
212+
}
213+
if_trait->is_inferred = is_inferred;
214+
215+
insert_into_if_traits_map(cur_node->data.str, if_trait);
216+
}
217+
218+
return SELINT_SUCCESS;
219+
}
220+
221+
enum selint_error infer_interfaces_deep(const struct policy_node *node)
222+
{
223+
for (const struct policy_node *cur_node = node; cur_node; cur_node = cur_node->next) {
224+
// skip non ifs
225+
if (cur_node->flavor != NODE_INTERFACE_DEF && cur_node->flavor != NODE_TEMP_DEF) {
226+
continue;
227+
}
228+
229+
const char *if_name = cur_node->data.str;
230+
struct interface_trait *if_trait = look_up_in_if_traits_map(if_name);
231+
232+
if (if_trait->is_inferred) {
233+
continue;
234+
}
235+
236+
enum selint_error ret = infer_interface(if_trait, cur_node->first_child, IN_DEEP);
237+
if (ret != SELINT_SUCCESS) {
238+
return ret;
239+
}
240+
for (int i = 0; i < TRAIT_MAX_PARAMETERS; ++i) {
241+
if (if_trait->parameters[i] == PARAM_UNKNOWN) {
242+
print_if_verbose("Parameter %d of %s %s not inferred\n",
243+
i + 1,
244+
trait_type_to_str(if_trait->type),
245+
if_trait->name);
246+
}
247+
}
248+
if_trait->is_inferred = true;
249+
}
250+
251+
return SELINT_SUCCESS;
252+
}
253+
254+
static void add_refpolicy_macro(const char *name, int param_count, const enum param_flavor flavors[])
255+
{
256+
struct interface_trait *if_trait = malloc(sizeof(struct interface_trait));
257+
if_trait->name = strdup(name);
258+
if_trait->type = MACRO_TRAIT;
259+
if_trait->is_inferred = true;
260+
if_trait->node = NULL;
261+
for (int i = 0; i < TRAIT_MAX_PARAMETERS; i++) {
262+
if (i < param_count) {
263+
if_trait->parameters[i] = flavors[i];
264+
} else {
265+
if_trait->parameters[i] = PARAM_INITIAL;
266+
}
267+
}
268+
269+
insert_into_if_traits_map(name, if_trait);
270+
}
271+
272+
enum selint_error infer_all_interfaces(const struct policy_file_list *files)
273+
{
274+
// manually insert common refpolicy macros, since macro definitions are not
275+
// part of the internal policy representation
276+
add_refpolicy_macro("can_exec",
277+
2,
278+
(enum param_flavor[2]){ PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE_OR_ATTRIBUTE });
279+
add_refpolicy_macro("filetrans_pattern",
280+
5,
281+
(enum param_flavor[5]){ PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE, PARAM_CLASS, PARAM_OBJECT_NAME });
282+
add_refpolicy_macro("filetrans_add_pattern",
283+
5,
284+
(enum param_flavor[5]){ PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE, PARAM_CLASS, PARAM_OBJECT_NAME });
285+
add_refpolicy_macro("domtrans_pattern",
286+
3,
287+
(enum param_flavor[3]){ PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE });
288+
add_refpolicy_macro("domain_auto_transition_pattern",
289+
3,
290+
(enum param_flavor[3]){ PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE });
291+
add_refpolicy_macro("admin_pattern",
292+
2,
293+
(enum param_flavor[2]){ PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE_OR_ATTRIBUTE });
294+
add_refpolicy_macro("stream_connect_pattern",
295+
4,
296+
(enum param_flavor[4]){ PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE_OR_ATTRIBUTE });
297+
add_refpolicy_macro("dgram_send_pattern",
298+
4,
299+
(enum param_flavor[4]){ PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE_OR_ATTRIBUTE, PARAM_TYPE_OR_ATTRIBUTE });
300+
301+
302+
// first infer only simple ifs; do not infer based on other called sub ifs
303+
print_if_verbose("Start shallow infer step...\n");
304+
for (const struct policy_file_node *cur_file = files->head; cur_file; cur_file = cur_file->next) {
305+
enum selint_error ret = infer_interfaces_shallow(cur_file->file->ast);
306+
if (ret != SELINT_SUCCESS) {
307+
return ret;
308+
}
309+
}
310+
311+
// on the second run the policy_nodes are linked to the traits, so we can infer deep
312+
print_if_verbose("Start deep infer step...\n");
313+
for (const struct policy_file_node *cur_file = files->head; cur_file; cur_file = cur_file->next) {
314+
enum selint_error ret = infer_interfaces_deep(cur_file->file->ast);
315+
if (ret != SELINT_SUCCESS) {
316+
return ret;
317+
}
318+
}
319+
320+
print_if_verbose("Finished infer steps\n");
321+
322+
return SELINT_SUCCESS;
323+
}
324+
325+
void free_interface_trait(struct interface_trait *to_free)
326+
{
327+
if (to_free == NULL) {
328+
return;
329+
}
330+
331+
free(to_free->name);
332+
free(to_free);
333+
}

0 commit comments

Comments
 (0)