Skip to content

Commit

Permalink
Expand checks about requirements to userspace classes
Browse files Browse the repository at this point in the history
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
  • Loading branch information
cgzones committed Dec 29, 2023
1 parent b13978d commit cea5660
Show file tree
Hide file tree
Showing 18 changed files with 283 additions and 11 deletions.
6 changes: 3 additions & 3 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,9 @@ CHECK IDS
S-009: Permission macro suffix does not match class name
S-010: Permission macro usage suggested

W-001: Type or attribute referenced without explicit declaration
W-002: Type, attribute or role used but not listed in require block in interface
W-003: Unused type, attribute or role listed in require block
W-001: Type, attribute or userspace class referenced without explicit declaration
W-002: Type, attribute, role or userspace class used but not listed in require block in interface
W-003: Unused type, attribute, role or userspace class listed in require block
W-004: Potentially unescaped regex character in file contexts paths
W-005: Interface call from module not in optional_policy block
W-006: Interface call with empty argument
Expand Down
22 changes: 21 additions & 1 deletion src/if_checks.c
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,13 @@ struct check_result *check_name_used_but_not_required_in_if(const struct
flavor = "Role Attribute";
} else if (name_is_role(ndata) && look_up_in_decl_map(ndata->name, DECL_ROLE)) {
flavor = "Role";
} else if (name_is_class(ndata) && look_up_in_decl_map(ndata->name, DECL_CLASS)) {
/* Ignore kernel classes */
if (!is_userspace_class(ndata->name, ndata->traits)) {
name_node = name_node->next;
continue;
}
flavor = "Class";
} else {
// This is a string we don't recognize. Other checks and/or
// the compiler catch invalid bare words
Expand Down Expand Up @@ -374,6 +381,14 @@ struct check_result *check_name_required_but_not_used_in_if(const struct
flavor = "Role Attribute";
} else if (dd->flavor == DECL_ROLE) {
flavor = "Role";
} else if (dd->flavor == DECL_CLASS) {
flavor = "Class";
if (!is_userspace_class(dd->name, dd->attrs)) {
return make_check_result('W',
W_ID_UNUSED_REQ,
"Class %s is listed in require block but is not a userspace class",
dd->name);
}
} else {
return NULL;
}
Expand All @@ -400,7 +415,12 @@ struct check_result *check_name_required_but_not_used_in_if(const struct
return NULL;
}

struct name_list *names_to_check = get_names_in_node(node);
struct name_list *names_to_check;
if (dd->flavor == DECL_CLASS) {
names_to_check = name_list_create(dd->name, NAME_CLASS);
} else {
names_to_check = get_names_in_node(node);
}
if (!names_to_check) {
// This should never happen
return alloc_internal_error(
Expand Down
22 changes: 22 additions & 0 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ int main(int argc, char **argv)
char *obj_perm_sets_path = NULL;
char *access_vector_path = NULL;
struct string_list *global_cond_files = NULL;
char *security_classes_path = NULL;

while (file) {
const char *suffix = (file->fts_pathlen > 3) ? (file->fts_path + file->fts_pathlen - 3) : NULL;
Expand Down Expand Up @@ -462,6 +463,10 @@ int main(int argc, char **argv)
&& (!strcmp(file->fts_name, "global_booleans") || !strcmp(file->fts_name, "global_tunables"))) {
// TODO: Make names configurable
global_cond_files = concat_string_lists(global_cond_files, sl_from_str(file->fts_path));
} else if (source_flag
&& !strcmp(file->fts_name, "security_classes")) {
// TODO: Make access_vectors name configurable
security_classes_path = strdup(file->fts_path);
} else {
// Directories might get traversed twice: preorder and final visit.
// Print only the final visit
Expand Down Expand Up @@ -533,6 +538,10 @@ int main(int argc, char **argv)
&& !str_in_sl(file->fts_path, global_cond_files)
&& (0 == strcmp(file->fts_name, "global_booleans") || 0 == strcmp(file->fts_name, "global_tunables"))) {
global_cond_files = concat_string_lists(global_cond_files, sl_from_str(file->fts_path));
} else if (source_flag
&& !security_classes_path
&& 0 == strcmp(file->fts_name, "security_classes")) {
security_classes_path = strdup(file->fts_path);
}
file = fts_read(ftsp);
}
Expand All @@ -557,6 +566,17 @@ int main(int argc, char **argv)
printf("%sWarning%s: Failed to locate access_vectors file.\n", color_warning(), color_reset());
}

if (security_classes_path) {
enum selint_error res = load_security_classes_source(security_classes_path);
if (res != SELINT_SUCCESS) {
printf("%sWarning%s: Failed to parse security_classes from %s: %d\n", color_warning(), color_reset(), security_classes_path, res);
} else {
print_if_verbose("Loaded security classes from %s\n", security_classes_path);
}
} else {
printf("%sWarning%s: Failed to locate security_classes file.\n", color_warning(), color_reset());
}

if (modules_conf_path) {
enum selint_error res =
load_modules_source(modules_conf_path);
Expand Down Expand Up @@ -630,13 +650,15 @@ int main(int argc, char **argv)
free_file_list(context_if_files);
free(obj_perm_sets_path);
free(access_vector_path);
free(security_classes_path);
free(modules_conf_path);
free_string_list(global_cond_files);
return EX_CONFIG;
}

free(obj_perm_sets_path);
free(access_vector_path);
free(security_classes_path);
free(modules_conf_path);
free_string_list(global_cond_files);

Expand Down
61 changes: 61 additions & 0 deletions src/maps.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ static struct hash_elem *perm_map = NULL;
static struct hash_elem *mods_map = NULL;
static struct hash_elem *mod_layers_map = NULL;
static struct if_hash_elem *interfaces_map = NULL;
static struct bool_hash_elem *userspace_class_map = NULL;
static struct sl_hash_elem *permmacros_map = NULL;
static struct template_hash_elem *template_map = NULL;

Expand Down Expand Up @@ -273,6 +274,56 @@ unsigned int decl_map_count(enum decl_flavor flavor)
}
}

no_sanitize_unsigned_integer_
void mark_userspace_class(const char *class_name)
{
struct bool_hash_elem *userspace_class;

HASH_FIND(hh_userspace_class, userspace_class_map, class_name, strlen(class_name), userspace_class);

if (!userspace_class) {
userspace_class = malloc(sizeof(struct bool_hash_elem));
userspace_class->key = strdup(class_name);
userspace_class->val = 1;
HASH_ADD_KEYPTR(hh_userspace_class, userspace_class_map, userspace_class->key,
strlen(userspace_class->key), userspace_class);
} else {
userspace_class->val = 1;
}
}

no_sanitize_unsigned_integer_
int is_userspace_class(const char *class_name, const struct string_list *permissions)
{
struct bool_hash_elem *userspace_class;
HASH_FIND(hh_userspace_class, userspace_class_map, class_name, strlen(class_name), userspace_class);
if (userspace_class && userspace_class->val == 1) {
return 1;
}

// the system class might be used by systemd depending on the permission
if (0 != strcmp(class_name, "system")) {
return 0;
}

for (const struct string_list *p = permissions; p; p = p->next) {
// if permission is not one of the kernel ones
// treat system as userspace class
if (0 != strcmp(p->string, "ipc_info") &&
0 != strcmp(p->string, "syslog_read") &&
0 != strcmp(p->string, "syslog_mod") &&
0 != strcmp(p->string, "syslog_console") &&
0 != strcmp(p->string, "module_request") &&
0 != strcmp(p->string, "module_load") &&
0 != strcmp(p->string, "*") &&
0 != strcmp(p->string, "~")) {
return 1;
}
}

return 0;
}

no_sanitize_unsigned_integer_
void mark_transform_if(const char *if_name)
{
Expand Down Expand Up @@ -583,6 +634,12 @@ unsigned int permmacros_map_count(void)
free(cur_decl); \
} \

#define FREE_BOOL_MAP(mn) HASH_ITER(hh_ ## mn, mn ## _map, cur_bool, tmp_bool) { \
HASH_DELETE(hh_ ## mn, mn ## _map, cur_bool); \
free(cur_bool->key); \
free(cur_bool); \
} \

#define FREE_IF_MAP(mn) HASH_ITER(hh_ ## mn, mn ## _map, cur_if, tmp_if) { \
HASH_DELETE(hh_ ## mn, mn ## _map, cur_if); \
free(cur_if->name); \
Expand Down Expand Up @@ -619,6 +676,10 @@ void free_all_maps(void)

FREE_IF_MAP(interfaces);

struct bool_hash_elem *cur_bool, *tmp_bool;

FREE_BOOL_MAP(userspace_class);

struct sl_hash_elem *cur_sl, *tmp_sl;

HASH_ITER(hh_permmacros, permmacros_map, cur_sl, tmp_sl) {
Expand Down
10 changes: 10 additions & 0 deletions src/maps.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ struct hash_elem {
hh_class, hh_perm, hh_mods, hh_mod_layers;
};

struct bool_hash_elem {
char *key;
int val;
UT_hash_handle hh_userspace_class;
};

struct sl_hash_elem {
char *key;
struct string_list *val;
Expand Down Expand Up @@ -71,6 +77,10 @@ void insert_into_ifs_map(const char *if_name, const char *mod_name);

const char *look_up_in_ifs_map(const char *if_name);

void mark_userspace_class(const char *class_name);

int is_userspace_class(const char *class_name, const struct string_list *permissions);

void mark_transform_if(const char *if_name);

int is_transform_if(const char *if_name);
Expand Down
70 changes: 70 additions & 0 deletions src/startup.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* limitations under the License.
*/

#include <ctype.h>
#include <errno.h>
#include <fts.h>
#include <stdio.h>
Expand Down Expand Up @@ -93,6 +94,75 @@ enum selint_error load_access_vectors_source(const char *av_path)
return SELINT_SUCCESS;
}

enum selint_error load_security_classes_source(const char *sec_class_path)
{
FILE *fd = fopen(sec_class_path, "r");

if (!fd) {
return SELINT_IO_ERROR;
}

enum selint_error ret = SELINT_SUCCESS;

char *line = NULL;
ssize_t len_read = 0;
size_t buf_len = 0;
while ((len_read = getline(&line, &buf_len, fd)) != -1) {
if (len_read <= 1 || line[0] == '#') {
continue;
}

const char *pos = line;
if (0 != strncmp(pos, "class", strlen("class"))) {
ret = SELINT_PARSE_ERROR;
break;
}
pos += strlen("class");

if (*pos != ' ' && *pos != '\t') {
ret = SELINT_PARSE_ERROR;
break;
}

while (*pos == ' ' || *pos == '\t') {
pos++;
}

if (!islower((unsigned char)*pos)) {
ret = SELINT_PARSE_ERROR;
break;
}

const char *name_start = pos;

while (*pos == '_' || islower((unsigned char)*pos) || isdigit((unsigned char)*pos)) {
pos++;
}

const char *name_end = pos;

while (isspace((unsigned char)*pos)) {
pos++;
}

if (*pos != '\0' && *pos != '#') {
ret = SELINT_PARSE_ERROR;
break;
}

if (*pos == '#' && NULL != strstr(pos, "userspace")) {
char *name = strndup(name_start, (size_t)(name_end - name_start));
mark_userspace_class(name);
free(name);
}
}

free(line);
fclose(fd);

return ret;
}

void load_modules_normal(void)
{

Expand Down
2 changes: 2 additions & 0 deletions src/startup.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ enum selint_error load_access_vectors_kernel(const char *av_path);

enum selint_error load_access_vectors_source(const char *av_path);

enum selint_error load_security_classes_source(const char *sec_class_path);

void load_modules_normal(void);

enum selint_error load_modules_source(const char *modules_conf_path);
Expand Down
22 changes: 18 additions & 4 deletions src/te_checks.c
Original file line number Diff line number Diff line change
Expand Up @@ -531,20 +531,34 @@ struct check_result *check_no_explicit_declaration(const struct check_data *data
flavor = DECL_ATTRIBUTE;
} else if (name_is_roleattr(ndata) && (mod_name = look_up_in_decl_map(ndata->name, DECL_ATTRIBUTE_ROLE))) {
flavor = DECL_ATTRIBUTE_ROLE;
} else if (name_is_class(ndata) && look_up_in_decl_map(name->data->name, DECL_CLASS)) {
// Ignore kernel classes
if (!is_userspace_class(name->data->name, name->data->traits)) {
continue;
}
flavor = DECL_CLASS;
mod_name = NULL;
// Do not check for roles: in refpolicy they are defined in the kernel module
// and used in role modules (like unprivuser)
} else {
//Not a known name
continue;
}

if (0 != strcmp(data->mod_name, mod_name)) {
if (!mod_name || 0 != strcmp(data->mod_name, mod_name)) {
// It may be required
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.",
ndata->name, mod_name);
struct check_result *to_ret;
if (flavor == DECL_CLASS) {
to_ret = make_check_result('W', W_ID_NO_EXPLICIT_DECL,
"No explicit declaration for userspace class %s. You should access it via interface call or use a require block.",
ndata->name);
} else {
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.",
ndata->name, mod_name);
}
free_name_list(names);
return to_ret;
}
Expand Down
5 changes: 5 additions & 0 deletions tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ FUNCTIONAL_TEST_FILES=functional/end-to-end.bats \
functional/policies/check_triggers/e10.warn.te \
functional/policies/check_triggers/modules.conf \
functional/policies/check_triggers/obj_perm_sets.spt \
functional/policies/check_triggers/security_classes \
functional/policies/check_triggers/s01.te \
functional/policies/check_triggers/s02.fc \
functional/policies/check_triggers/s02_other.te \
Expand All @@ -168,7 +169,11 @@ FUNCTIONAL_TEST_FILES=functional/end-to-end.bats \
functional/policies/check_triggers/w02_role.te \
functional/policies/check_triggers/w02.te \
functional/policies/check_triggers/w02.bad_if.if \
functional/policies/check_triggers/w02_system_nowarn.if \
functional/policies/check_triggers/w02_system_warn.if \
functional/policies/check_triggers/w03_alias.if \
functional/policies/check_triggers/w03_system_nowarn.if \
functional/policies/check_triggers/w03_system_warn.if \
functional/policies/check_triggers/w03.if \
functional/policies/check_triggers/w03_role.if \
functional/policies/check_triggers/w03_stub.if \
Expand Down
Loading

0 comments on commit cea5660

Please sign in to comment.