Skip to content

Commit f296281

Browse files
committed
Expand checks about requirements to userspace classes
Refpolicy findings: unconfined.te: 63: (W): No explicit declaration for userspace class system. You should access it via interface call or use a require block. (W-001) systemd.te: 1170: (W): No explicit declaration for userspace class dbus. You should access it via interface call or use a require block. (W-001) systemd.te: 1282: (W): No explicit declaration for userspace class dbus. You should access it via interface call or use a require block. (W-001) init.te: 261: (W): No explicit declaration for userspace class system. You should access it via interface call or use a require block. (W-001) init.te: 302: (W): No explicit declaration for userspace class service. You should access it via interface call or use a require block. (W-001) init.te: 1094: (W): No explicit declaration for userspace class system. You should access it via interface call or use a require block. (W-001) init.te: 1102: (W): No explicit declaration for userspace class service. You should access it via interface call or use a require block. (W-001) init.te: 1110: (W): No explicit declaration for userspace class service. You should access it via interface call or use a require block. (W-001) init.te: 1114: (W): No explicit declaration for userspace class service. You should access it via interface call or use a require block. (W-001) init.te: 1115: (W): No explicit declaration for userspace class service. You should access it via interface call or use a require block. (W-001) devicekit.te: 56: (W): No explicit declaration for userspace class dbus. You should access it via interface call or use a require block. (W-001) devicekit.te: 157: (W): No explicit declaration for userspace class dbus. You should access it via interface call or use a require block. (W-001) devicekit.te: 297: (W): No explicit declaration for userspace class dbus. You should access it via interface call or use a require block. (W-001) kernel.te: 558: (W): No explicit declaration for userspace class system. You should access it via interface call or use a require block. (W-001) chromium.if: 139: (W): Class dbus is listed in require block but not used in interface (W-003) init.if: 1200: (W): Class system is used in interface but not required (W-002) init.if: 1218: (W): Class system is used in interface but not required (W-002) init.if: 1236: (W): Class system is used in interface but not required (W-002) init.if: 1254: (W): Class system is used in interface but not required (W-002) init.if: 1272: (W): Class system is used in interface but not required (W-002) init.if: 1290: (W): Class system is used in interface but not required (W-002) init.if: 1308: (W): Class system is used in interface but not required (W-002) init.if: 1326: (W): Class system is used in interface but not required (W-002) init.if: 1401: (W): Class bpf is listed in require block but is not a userspace class (W-003) systemd.if: 148: (W): Class system is used in interface but not required (W-002) systemd.if: 158: (W): Class service is used in interface but not required (W-002) systemd.if: 159: (W): Class service is used in interface but not required (W-002) systemd.if: 391: (W): Class system is used in interface but not required (W-002) systemd.if: 415: (W): Class system is used in interface but not required (W-002) systemd.if: 439: (W): Class system is used in interface but not required (W-002) unconfined.if: 34: (W): Class service is listed in require block but not used in interface (W-003) xserver.if: 353: (W): Class x_property is listed in require block but not used in interface (W-003) postgresql.if: 31: (W): Class db_database is listed in require block but not used in interface (W-003) postgresql.if: 37: (W): Class db_language is listed in require block but not used in interface (W-003) postgresql.if: 465: (W): Class db_database is listed in require block but not used in interface (W-003) postgresql.if: 471: (W): Class db_language is listed in require block but not used in interface (W-003) Found the following issue counts: W-001: 14 W-002: 14 W-003: 8
1 parent a8c1050 commit f296281

File tree

18 files changed

+283
-11
lines changed

18 files changed

+283
-11
lines changed

README

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,9 +175,9 @@ CHECK IDS
175175
S-009: Permission macro suffix does not match class name
176176
S-010: Permission macro usage suggested
177177

178-
W-001: Type or attribute referenced without explicit declaration
179-
W-002: Type, attribute or role used but not listed in require block in interface
180-
W-003: Unused type, attribute or role listed in require block
178+
W-001: Type, attribute or userspace class referenced without explicit declaration
179+
W-002: Type, attribute, role or userspace class used but not listed in require block in interface
180+
W-003: Unused type, attribute, role or userspace class listed in require block
181181
W-004: Potentially unescaped regex character in file contexts paths
182182
W-005: Interface call from module not in optional_policy block
183183
W-006: Interface call with empty argument

src/if_checks.c

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,13 @@ struct check_result *check_name_used_but_not_required_in_if(const struct
330330
flavor = "Role Attribute";
331331
} else if (name_is_role(ndata) && look_up_in_decl_map(ndata->name, DECL_ROLE)) {
332332
flavor = "Role";
333+
} else if (name_is_class(ndata) && look_up_in_decl_map(ndata->name, DECL_CLASS)) {
334+
/* Ignore kernel classes */
335+
if (!is_userspace_class(ndata->name, ndata->traits)) {
336+
name_node = name_node->next;
337+
continue;
338+
}
339+
flavor = "Class";
333340
} else {
334341
// This is a string we don't recognize. Other checks and/or
335342
// the compiler catch invalid bare words
@@ -374,6 +381,14 @@ struct check_result *check_name_required_but_not_used_in_if(const struct
374381
flavor = "Role Attribute";
375382
} else if (dd->flavor == DECL_ROLE) {
376383
flavor = "Role";
384+
} else if (dd->flavor == DECL_CLASS) {
385+
flavor = "Class";
386+
if (!is_userspace_class(dd->name, dd->attrs)) {
387+
return make_check_result('W',
388+
W_ID_UNUSED_REQ,
389+
"Class %s is listed in require block but is not a userspace class",
390+
dd->name);
391+
}
377392
} else {
378393
return NULL;
379394
}
@@ -400,7 +415,12 @@ struct check_result *check_name_required_but_not_used_in_if(const struct
400415
return NULL;
401416
}
402417

403-
struct name_list *names_to_check = get_names_in_node(node);
418+
struct name_list *names_to_check;
419+
if (dd->flavor == DECL_CLASS) {
420+
names_to_check = name_list_create(dd->name, NAME_CLASS);
421+
} else {
422+
names_to_check = get_names_in_node(node);
423+
}
404424
if (!names_to_check) {
405425
// This should never happen
406426
return alloc_internal_error(

src/main.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,7 @@ int main(int argc, char **argv)
411411
char *obj_perm_sets_path = NULL;
412412
char *access_vector_path = NULL;
413413
struct string_list *global_cond_files = NULL;
414+
char *security_classes_path = NULL;
414415

415416
while (file) {
416417
const char *suffix = (file->fts_pathlen > 3) ? (file->fts_path + file->fts_pathlen - 3) : NULL;
@@ -447,6 +448,10 @@ int main(int argc, char **argv)
447448
&& (!strcmp(file->fts_name, "global_booleans") || !strcmp(file->fts_name, "global_tunables"))) {
448449
// TODO: Make names configurable
449450
global_cond_files = concat_string_lists(global_cond_files, sl_from_str(file->fts_path));
451+
} else if (source_flag
452+
&& !strcmp(file->fts_name, "security_classes")) {
453+
// TODO: Make access_vectors name configurable
454+
security_classes_path = strdup(file->fts_path);
450455
} else {
451456
// Directories might get traversed twice: preorder and final visit.
452457
// Print only the final visit
@@ -518,6 +523,10 @@ int main(int argc, char **argv)
518523
&& !str_in_sl(file->fts_path, global_cond_files)
519524
&& (0 == strcmp(file->fts_name, "global_booleans") || 0 == strcmp(file->fts_name, "global_tunables"))) {
520525
global_cond_files = concat_string_lists(global_cond_files, sl_from_str(file->fts_path));
526+
} else if (source_flag
527+
&& !security_classes_path
528+
&& 0 == strcmp(file->fts_name, "security_classes")) {
529+
security_classes_path = strdup(file->fts_path);
521530
}
522531
file = fts_read(ftsp);
523532
}
@@ -542,6 +551,17 @@ int main(int argc, char **argv)
542551
printf("%sWarning%s: Failed to locate access_vectors file.\n", color_warning(), color_reset());
543552
}
544553

554+
if (security_classes_path) {
555+
enum selint_error res = load_security_classes_source(security_classes_path);
556+
if (res != SELINT_SUCCESS) {
557+
printf("%sWarning%s: Failed to parse security_classes from %s: %d\n", color_warning(), color_reset(), security_classes_path, res);
558+
} else {
559+
print_if_verbose("Loaded security classes from %s\n", security_classes_path);
560+
}
561+
} else {
562+
printf("%sWarning%s: Failed to locate security_classes file.\n", color_warning(), color_reset());
563+
}
564+
545565
if (modules_conf_path) {
546566
enum selint_error res =
547567
load_modules_source(modules_conf_path);
@@ -615,13 +635,15 @@ int main(int argc, char **argv)
615635
free_file_list(context_if_files);
616636
free(obj_perm_sets_path);
617637
free(access_vector_path);
638+
free(security_classes_path);
618639
free(modules_conf_path);
619640
free_string_list(global_cond_files);
620641
return EX_CONFIG;
621642
}
622643

623644
free(obj_perm_sets_path);
624645
free(access_vector_path);
646+
free(security_classes_path);
625647
free(modules_conf_path);
626648
free_string_list(global_cond_files);
627649

src/maps.c

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ static struct hash_elem *perm_map = NULL;
3838
static struct hash_elem *mods_map = NULL;
3939
static struct hash_elem *mod_layers_map = NULL;
4040
static struct if_hash_elem *interfaces_map = NULL;
41+
static struct bool_hash_elem *userspace_class_map = NULL;
4142
static struct sl_hash_elem *permmacros_map = NULL;
4243
static struct template_hash_elem *template_map = NULL;
4344

@@ -273,6 +274,56 @@ unsigned int decl_map_count(enum decl_flavor flavor)
273274
}
274275
}
275276

277+
no_sanitize_unsigned_integer_
278+
void mark_userspace_class(const char *class_name)
279+
{
280+
struct bool_hash_elem *userspace_class;
281+
282+
HASH_FIND(hh_userspace_class, userspace_class_map, class_name, strlen(class_name), userspace_class);
283+
284+
if (!userspace_class) {
285+
userspace_class = malloc(sizeof(struct bool_hash_elem));
286+
userspace_class->key = strdup(class_name);
287+
userspace_class->val = 1;
288+
HASH_ADD_KEYPTR(hh_userspace_class, userspace_class_map, userspace_class->key,
289+
strlen(userspace_class->key), userspace_class);
290+
} else {
291+
userspace_class->val = 1;
292+
}
293+
}
294+
295+
no_sanitize_unsigned_integer_
296+
int is_userspace_class(const char *class_name, const struct string_list *permissions)
297+
{
298+
struct bool_hash_elem *userspace_class;
299+
HASH_FIND(hh_userspace_class, userspace_class_map, class_name, strlen(class_name), userspace_class);
300+
if (userspace_class && userspace_class->val == 1) {
301+
return 1;
302+
}
303+
304+
// the system class might be used by systemd depending on the permission
305+
if (0 != strcmp(class_name, "system")) {
306+
return 0;
307+
}
308+
309+
for (const struct string_list *p = permissions; p; p = p->next) {
310+
// if permission is not one of the kernel ones
311+
// treat system as userspace class
312+
if (0 != strcmp(p->string, "ipc_info") &&
313+
0 != strcmp(p->string, "syslog_read") &&
314+
0 != strcmp(p->string, "syslog_mod") &&
315+
0 != strcmp(p->string, "syslog_console") &&
316+
0 != strcmp(p->string, "module_request") &&
317+
0 != strcmp(p->string, "module_load") &&
318+
0 != strcmp(p->string, "*") &&
319+
0 != strcmp(p->string, "~")) {
320+
return 1;
321+
}
322+
}
323+
324+
return 0;
325+
}
326+
276327
no_sanitize_unsigned_integer_
277328
void mark_transform_if(const char *if_name)
278329
{
@@ -583,6 +634,12 @@ unsigned int permmacros_map_count(void)
583634
free(cur_decl); \
584635
} \
585636

637+
#define FREE_BOOL_MAP(mn) HASH_ITER(hh_ ## mn, mn ## _map, cur_bool, tmp_bool) { \
638+
HASH_DELETE(hh_ ## mn, mn ## _map, cur_bool); \
639+
free(cur_bool->key); \
640+
free(cur_bool); \
641+
} \
642+
586643
#define FREE_IF_MAP(mn) HASH_ITER(hh_ ## mn, mn ## _map, cur_if, tmp_if) { \
587644
HASH_DELETE(hh_ ## mn, mn ## _map, cur_if); \
588645
free(cur_if->name); \
@@ -619,6 +676,10 @@ void free_all_maps(void)
619676

620677
FREE_IF_MAP(interfaces);
621678

679+
struct bool_hash_elem *cur_bool, *tmp_bool;
680+
681+
FREE_BOOL_MAP(userspace_class);
682+
622683
struct sl_hash_elem *cur_sl, *tmp_sl;
623684

624685
HASH_ITER(hh_permmacros, permmacros_map, cur_sl, tmp_sl) {

src/maps.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ struct hash_elem {
2929
hh_class, hh_perm, hh_mods, hh_mod_layers;
3030
};
3131

32+
struct bool_hash_elem {
33+
char *key;
34+
int val;
35+
UT_hash_handle hh_userspace_class;
36+
};
37+
3238
struct sl_hash_elem {
3339
char *key;
3440
struct string_list *val;
@@ -71,6 +77,10 @@ void insert_into_ifs_map(const char *if_name, const char *mod_name);
7177

7278
const char *look_up_in_ifs_map(const char *if_name);
7379

80+
void mark_userspace_class(const char *class_name);
81+
82+
int is_userspace_class(const char *class_name, const struct string_list *permissions);
83+
7484
void mark_transform_if(const char *if_name);
7585

7686
int is_transform_if(const char *if_name);

src/startup.c

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17+
#include <ctype.h>
1718
#include <errno.h>
1819
#include <fts.h>
1920
#include <stdio.h>
@@ -93,6 +94,75 @@ enum selint_error load_access_vectors_source(const char *av_path)
9394
return SELINT_SUCCESS;
9495
}
9596

97+
enum selint_error load_security_classes_source(const char *sec_class_path)
98+
{
99+
FILE *fd = fopen(sec_class_path, "r");
100+
101+
if (!fd) {
102+
return SELINT_IO_ERROR;
103+
}
104+
105+
enum selint_error ret = SELINT_SUCCESS;
106+
107+
char *line = NULL;
108+
ssize_t len_read = 0;
109+
size_t buf_len = 0;
110+
while ((len_read = getline(&line, &buf_len, fd)) != -1) {
111+
if (len_read <= 1 || line[0] == '#') {
112+
continue;
113+
}
114+
115+
const char *pos = line;
116+
if (0 != strncmp(pos, "class", strlen("class"))) {
117+
ret = SELINT_PARSE_ERROR;
118+
break;
119+
}
120+
pos += strlen("class");
121+
122+
if (*pos != ' ' && *pos != '\t') {
123+
ret = SELINT_PARSE_ERROR;
124+
break;
125+
}
126+
127+
while (*pos == ' ' || *pos == '\t') {
128+
pos++;
129+
}
130+
131+
if (!islower((unsigned char)*pos)) {
132+
ret = SELINT_PARSE_ERROR;
133+
break;
134+
}
135+
136+
const char *name_start = pos;
137+
138+
while (*pos == '_' || islower((unsigned char)*pos) || isdigit((unsigned char)*pos)) {
139+
pos++;
140+
}
141+
142+
const char *name_end = pos;
143+
144+
while (isspace((unsigned char)*pos)) {
145+
pos++;
146+
}
147+
148+
if (*pos != '\0' && *pos != '#') {
149+
ret = SELINT_PARSE_ERROR;
150+
break;
151+
}
152+
153+
if (*pos == '#' && NULL != strstr(pos, "userspace")) {
154+
char *name = strndup(name_start, (size_t)(name_end - name_start));
155+
mark_userspace_class(name);
156+
free(name);
157+
}
158+
}
159+
160+
free(line);
161+
fclose(fd);
162+
163+
return ret;
164+
}
165+
96166
void load_modules_normal(void)
97167
{
98168

src/startup.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ enum selint_error load_access_vectors_kernel(const char *av_path);
2525

2626
enum selint_error load_access_vectors_source(const char *av_path);
2727

28+
enum selint_error load_security_classes_source(const char *sec_class_path);
29+
2830
void load_modules_normal(void);
2931

3032
enum selint_error load_modules_source(const char *modules_conf_path);

src/te_checks.c

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -531,20 +531,34 @@ struct check_result *check_no_explicit_declaration(const struct check_data *data
531531
flavor = DECL_ATTRIBUTE;
532532
} else if (name_is_roleattr(ndata) && (mod_name = look_up_in_decl_map(ndata->name, DECL_ATTRIBUTE_ROLE))) {
533533
flavor = DECL_ATTRIBUTE_ROLE;
534+
} else if (name_is_class(ndata) && look_up_in_decl_map(name->data->name, DECL_CLASS)) {
535+
// Ignore kernel classes
536+
if (!is_userspace_class(name->data->name, name->data->traits)) {
537+
continue;
538+
}
539+
flavor = DECL_CLASS;
540+
mod_name = NULL;
534541
// Do not check for roles: in refpolicy they are defined in the kernel module
535542
// and used in role modules (like unprivuser)
536543
} else {
537544
//Not a known name
538545
continue;
539546
}
540547

541-
if (0 != strcmp(data->mod_name, mod_name)) {
548+
if (!mod_name || 0 != strcmp(data->mod_name, mod_name)) {
542549
// It may be required
543550
if (!has_require(node, ndata->name, flavor)) {
544551
// We didn't find a require block with this name
545-
struct check_result *to_ret = make_check_result('W', W_ID_NO_EXPLICIT_DECL,
546-
"No explicit declaration for %s from module %s. You should access it via interface call or use a require block.",
547-
ndata->name, mod_name);
552+
struct check_result *to_ret;
553+
if (flavor == DECL_CLASS) {
554+
to_ret = make_check_result('W', W_ID_NO_EXPLICIT_DECL,
555+
"No explicit declaration for userspace class %s. You should access it via interface call or use a require block.",
556+
ndata->name);
557+
} else {
558+
to_ret = make_check_result('W', W_ID_NO_EXPLICIT_DECL,
559+
"No explicit declaration for %s from module %s. You should access it via interface call or use a require block.",
560+
ndata->name, mod_name);
561+
}
548562
free_name_list(names);
549563
return to_ret;
550564
}

tests/Makefile.am

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ FUNCTIONAL_TEST_FILES=functional/end-to-end.bats \
148148
functional/policies/check_triggers/e10.warn.te \
149149
functional/policies/check_triggers/modules.conf \
150150
functional/policies/check_triggers/obj_perm_sets.spt \
151+
functional/policies/check_triggers/security_classes \
151152
functional/policies/check_triggers/s01.te \
152153
functional/policies/check_triggers/s02.fc \
153154
functional/policies/check_triggers/s02_other.te \
@@ -166,7 +167,11 @@ FUNCTIONAL_TEST_FILES=functional/end-to-end.bats \
166167
functional/policies/check_triggers/w02_role.te \
167168
functional/policies/check_triggers/w02.te \
168169
functional/policies/check_triggers/w02.bad_if.if \
170+
functional/policies/check_triggers/w02_system_nowarn.if \
171+
functional/policies/check_triggers/w02_system_warn.if \
169172
functional/policies/check_triggers/w03_alias.if \
173+
functional/policies/check_triggers/w03_system_nowarn.if \
174+
functional/policies/check_triggers/w03_system_warn.if \
170175
functional/policies/check_triggers/w03.if \
171176
functional/policies/check_triggers/w03_role.if \
172177
functional/policies/check_triggers/w03_stub.if \

0 commit comments

Comments
 (0)