Skip to content
Snippets Groups Projects
Commit ad3cb39e authored by William Roberts's avatar William Roberts Committed by Nick Kralevich
Browse files

checkfc: add attribute test


Enable checkfc to check *_contexts against a set of valid attributes
which must be associated with all types in the contexts file that
is being checked.

Since it's imperative that checkfc knows which file its checking to
choose the proper attribute set, the -s option is introduced to
indicate the service_contexts file. The property_contexts file continues
to use the existing -p and file_contexts requires no specification, aka
it's the default.

Failure examples:
file_contexts:
Error: type "init" is not of set: "fs_type, dev_type, file_type"

service_contexts:
Error: type "init_exec" is not of set: "service_manager_type"

property_contexts:
Error: type "bluetooth_service" is not of set: "property_type"

Change-Id: I62077e4d0760858a9459e753e14dfd209868080f
Signed-off-by: default avatarWilliam Roberts <william.c.roberts@intel.com>
parent a9bf9954
Branches
Tags
No related merge requests found
......@@ -351,7 +351,7 @@ $(service_contexts.tmp): $(all_svc_files) $(all_svcfiles_with_nl) $(built_nl)
$(LOCAL_BUILT_MODULE): PRIVATE_SEPOLICY := $(built_sepolicy)
$(LOCAL_BUILT_MODULE): $(service_contexts.tmp) $(built_sepolicy) $(HOST_OUT_EXECUTABLES)/checkfc $(ACP)
@mkdir -p $(dir $@)
$(hide) $(HOST_OUT_EXECUTABLES)/checkfc -p $(PRIVATE_SEPOLICY) $<
$(hide) $(HOST_OUT_EXECUTABLES)/checkfc -s $(PRIVATE_SEPOLICY) $<
$(hide) $(ACP) $< $@
built_svc := $(LOCAL_BUILT_MODULE)
......@@ -375,7 +375,7 @@ $(general_service_contexts.tmp): $(addprefix $(LOCAL_PATH)/, service_contexts)
$(LOCAL_BUILT_MODULE): PRIVATE_SEPOLICY := $(built_general_sepolicy)
$(LOCAL_BUILT_MODULE): $(general_service_contexts.tmp) $(built_general_sepolicy) $(HOST_OUT_EXECUTABLES)/checkfc $(ACP)
@mkdir -p $(dir $@)
$(hide) $(HOST_OUT_EXECUTABLES)/checkfc -p $(PRIVATE_SEPOLICY) $<
$(hide) $(HOST_OUT_EXECUTABLES)/checkfc -s $(PRIVATE_SEPOLICY) $<
$(hide) $(ACP) $< $@
general_service_contexts.tmp :=
......
......@@ -3,6 +3,8 @@
#
# All types used for devices.
# On change, update CHECK_FC_ASSERT_ATTRS
# in tools/checkfc.c
attribute dev_type;
# All types used for processes.
......@@ -19,6 +21,8 @@ attribute domain;
attribute domain_deprecated;
# All types used for filesystems.
# On change, update CHECK_FC_ASSERT_ATTRS
# definition in tools/checkfc.c.
attribute fs_type;
# All types used for context= mounts.
......@@ -26,6 +30,8 @@ attribute contextmount_type;
# All types used for files that can exist on a labeled fs.
# Do not use for pseudo file types.
# On change, update CHECK_FC_ASSERT_ATTRS
# definition in tools/checkfc.c.
attribute file_type;
# All types used for domain entry points.
......@@ -53,6 +59,8 @@ attribute netif_type;
attribute port_type;
# All types used for property service
# On change, update CHECK_PC_ASSERT_ATTRS
# definition in tools/checkfc.c.
attribute property_type;
# All properties defined in core SELinux policy. Should not be
......@@ -69,6 +77,8 @@ attribute app_api_service;
attribute system_api_service;
# All types used for services managed by service_manager.
# On change, update CHECK_SC_ASSERT_ATTRS
# definition in tools/checkfc.c.
attribute service_manager_type;
# All domains that can override MLS restrictions.
......
#include <getopt.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <sepol/module.h>
#include <sepol/policydb/policydb.h>
#include <sepol/sepol.h>
#include <selinux/selinux.h>
#include <selinux/label.h>
static int nerr;
static const char * const CHECK_FC_ASSERT_ATTRS[] = { "fs_type", "dev_type", "file_type", NULL };
static const char * const CHECK_PC_ASSERT_ATTRS[] = { "property_type", NULL };
static const char * const CHECK_SC_ASSERT_ATTRS[] = { "service_manager_type", NULL };
typedef enum filemode filemode;
enum filemode {
filemode_file_contexts = 0,
filemode_property_contexts,
filemode_service_contexts
};
static struct {
/* policy */
struct {
union {
/* Union these so we don't have to cast */
sepol_policydb_t *sdb;
policydb_t *pdb;
};
sepol_policy_file_t *pf;
sepol_handle_t *handle;
FILE *file;
#define SEHANDLE_CNT 2
struct selabel_handle *sehnd[SEHANDLE_CNT];
} sepolicy;
/* assertions */
struct {
const char * const *attrs; /* for the original set to print on error */
ebitmap_t set; /* the ebitmap representation of the attrs */
} assert;
} global_state;
static const char * const *filemode_to_assert_attrs(filemode mode)
{
switch (mode) {
case filemode_file_contexts:
return CHECK_FC_ASSERT_ATTRS;
case filemode_property_contexts:
return CHECK_PC_ASSERT_ATTRS;
case filemode_service_contexts:
return CHECK_SC_ASSERT_ATTRS;
}
/* die on invalid parameters */
fprintf(stderr, "Error: Invalid mode of operation: %d\n", mode);
exit(1);
}
static int get_attr_bit(policydb_t *policydb, const char *attr_name)
{
struct type_datum *attr = hashtab_search(policydb->p_types.table, (char *)attr_name);
if (!attr) {
fprintf(stderr, "Error: \"%s\" is not defined in this policy.\n", attr_name);
return -1;
}
if (attr->flavor != TYPE_ATTRIB) {
fprintf(stderr, "Error: \"%s\" is not an attribute in this policy.\n", attr_name);
return -1;
}
return attr->s.value - 1;
}
static bool ebitmap_attribute_assertion_init(ebitmap_t *assertions, const char * const attributes[])
{
while (*attributes) {
int bit_pos = get_attr_bit(global_state.sepolicy.pdb, *attributes);
if (bit_pos < 0) {
/* get_attr_bit() logs error */
return false;
}
int err = ebitmap_set_bit(assertions, bit_pos, 1);
if (err) {
fprintf(stderr, "Error: setting bit on assertion ebitmap!\n");
return false;
}
attributes++;
}
return true;
}
static bool is_type_of_attribute_set(policydb_t *policydb, const char *type_name,
ebitmap_t *attr_set)
{
struct type_datum *type = hashtab_search(policydb->p_types.table, (char *)type_name);
if (!type) {
fprintf(stderr, "Error: \"%s\" is not defined in this policy.\n", type_name);
return false;
}
if (type->flavor != TYPE_TYPE) {
fprintf(stderr, "Error: \"%s\" is not a type in this policy.\n", type_name);
return false;
}
ebitmap_t dst;
ebitmap_init(&dst);
/* Take the intersection, if the set is empty, then its a failure */
int rc = ebitmap_and(&dst, attr_set, &policydb->type_attr_map[type->s.value - 1]);
if (rc) {
fprintf(stderr, "Error: Could not perform ebitmap_and: %d\n", rc);
exit(1);
}
bool res = (bool)ebitmap_length(&dst);
ebitmap_destroy(&dst);
return res;
}
static void dump_char_array(FILE *stream, const char * const *strings)
{
const char * const *p = strings;
fprintf(stream, "\"");
while (*p) {
const char *s = *p++;
const char *fmt = *p ? "%s, " : "%s\"";
fprintf(stream, fmt, s);
}
}
static int validate(char **contextp)
{
bool res;
char *context = *contextp;
if (sepol_check_context(context) < 0) {
nerr++;
return -1;
sepol_context_t *ctx;
int rc = sepol_context_from_string(global_state.sepolicy.handle, context,
&ctx);
if (rc < 0) {
fprintf(stderr, "Error: Could not allocate context from string");
exit(1);
}
return 0;
rc = sepol_context_check(global_state.sepolicy.handle,
global_state.sepolicy.sdb, ctx);
if (rc < 0) {
goto out;
}
static void usage(char *name) {
fprintf(stderr, "usage1: %s [-p] sepolicy context_file\n\n", name);
fprintf(stderr, "Parses a context file and checks for syntax errors.\n");
fprintf(stderr, "The context_file is assumed to be a file_contexts file\n");
fprintf(stderr, "unless the -p option is used to indicate the property backend.\n\n");
const char *type_name = sepol_context_get_type(ctx);
fprintf(stderr, "usage2: %s -c file_contexts1 file_contexts2\n\n", name);
fprintf(stderr, "Compares two file contexts files and reports one of subset, equal, superset, or incomparable.\n");
uint32_t len = ebitmap_length(&global_state.assert.set);
if (len > 0) {
res = !is_type_of_attribute_set(global_state.sepolicy.pdb, type_name,
&global_state.assert.set);
if (res) {
fprintf(stderr, "Error: type \"%s\" is not of set: ", type_name);
dump_char_array(stderr, global_state.assert.attrs);
fprintf(stderr, "\n");
/* The calls above did not affect rc, so set error before going to out */
rc = -1;
goto out;
}
}
/* Success: Although it should be 0, we explicitly set rc to 0 for clarity */
rc = 0;
out:
sepol_context_free(ctx);
return rc;
}
static void usage(char *name) {
fprintf(stderr, "usage1: %s [-p|-s] sepolicy context_file\n\n"
"Parses a context file and checks for syntax errors.\n"
"The context_file is assumed to be a file_contexts file\n"
"unless the -p or -s option is used to indicate the property or service backend respectively.\n\n"
"usage2: %s -c file_contexts1 file_contexts2\n\n"
"Compares two file contexts files and reports one of subset, equal, superset, or incomparable.\n\n",
name, name);
exit(1);
}
static void cleanup(void) {
if (global_state.sepolicy.file) {
fclose(global_state.sepolicy.file);
}
if (global_state.sepolicy.sdb) {
sepol_policydb_free(global_state.sepolicy.sdb);
}
if (global_state.sepolicy.pf) {
sepol_policy_file_free(global_state.sepolicy.pf);
}
if (global_state.sepolicy.handle) {
sepol_handle_destroy(global_state.sepolicy.handle);
}
ebitmap_destroy(&global_state.assert.set);
int i;
for (i = 0; i < SEHANDLE_CNT; i++) {
struct selabel_handle *sehnd = global_state.sepolicy.sehnd[i];
if (sehnd) {
selabel_close(sehnd);
}
}
}
static void do_compare_and_die_on_error(struct selinux_opt opts[], unsigned int backend, char *paths[])
{
enum selabel_cmp_result result;
char *result_str[] = { "subset", "equal", "superset", "incomparable" };
int i;
opts[0].value = NULL; /* not validating against a policy when comparing */
for (i = 0; i < SEHANDLE_CNT; i++) {
opts[1].value = paths[i];
global_state.sepolicy.sehnd[i] = selabel_open(backend, opts, 2);
if (!global_state.sepolicy.sehnd[i]) {
fprintf(stderr, "Error: could not load context file from %s\n", paths[i]);
exit(1);
}
}
result = selabel_cmp(global_state.sepolicy.sehnd[0], global_state.sepolicy.sehnd[1]);
printf("%s\n", result_str[result]);
}
static void do_fc_check_and_die_on_error(struct selinux_opt opts[], unsigned int backend, filemode mode,
const char *sepolicy_file, const char *context_file)
{
global_state.sepolicy.file = fopen(sepolicy_file, "r");
if (!global_state.sepolicy.file) {
perror("Error: could not open policy file");
exit(1);
}
global_state.sepolicy.handle = sepol_handle_create();
if (!global_state.sepolicy.handle) {
fprintf(stderr, "Error: could not create policy handle: %s\n", strerror(errno));
exit(1);
}
if (sepol_policy_file_create(&global_state.sepolicy.pf) < 0) {
perror("Error: could not create policy handle");
exit(1);
}
sepol_policy_file_set_fp(global_state.sepolicy.pf, global_state.sepolicy.file);
sepol_policy_file_set_handle(global_state.sepolicy.pf, global_state.sepolicy.handle);
int rc = sepol_policydb_create(&global_state.sepolicy.sdb);
if (rc < 0) {
perror("Error: could not create policy db");
exit(1);
}
rc = sepol_policydb_read(global_state.sepolicy.sdb, global_state.sepolicy.pf);
if (rc < 0) {
perror("Error: could not read file into policy db");
exit(1);
}
global_state.assert.attrs = filemode_to_assert_attrs(mode);
bool ret = ebitmap_attribute_assertion_init(&global_state.assert.set, global_state.assert.attrs);
if (!ret) {
/* error messages logged by ebitmap_attribute_assertion_init() */
exit(1);
}
selinux_set_callback(SELINUX_CB_VALIDATE,
(union selinux_callback)&validate);
opts[1].value = context_file;
global_state.sepolicy.sehnd[0] = selabel_open(backend, opts, 2);
if (!global_state.sepolicy.sehnd[0]) {
fprintf(stderr, "Error: could not load context file from %s\n", context_file);
exit(1);
}
}
int main(int argc, char **argv)
{
struct selinux_opt opts[] = {
......@@ -40,17 +308,22 @@ int main(int argc, char **argv)
// Default backend unless changed by input argument.
unsigned int backend = SELABEL_CTX_FILE;
FILE *fp;
bool compare = false;
struct selabel_handle *sehnd[2];
char c;
while ((c = getopt(argc, argv, "cph")) != -1) {
filemode mode = filemode_file_contexts;
while ((c = getopt(argc, argv, "cps")) != -1) {
switch (c) {
case 'c':
compare = true;
break;
case 'p':
mode = filemode_property_contexts;
backend = SELABEL_CTX_ANDROID_PROP;
break;
case 's':
mode = filemode_service_contexts;
backend = SELABEL_CTX_ANDROID_PROP;
break;
case 'h':
......@@ -69,57 +342,16 @@ int main(int argc, char **argv)
usage(argv[0]);
}
if (compare) {
enum selabel_cmp_result result;
char *result_str[] = { "subset", "equal", "superset", "incomparable" };
int i;
opts[0].value = NULL; /* not validating against a policy when comparing */
for (i = 0; i < 2; i++) {
opts[1].value = argv[index+i];
sehnd[i] = selabel_open(backend, opts, 2);
if (!sehnd[i]) {
fprintf(stderr, "Error loading context file from %s\n", argv[index+i]);
exit(1);
}
}
atexit(cleanup);
result = selabel_cmp(sehnd[0], sehnd[1]);
for (i = 0; i < 2; i++)
selabel_close(sehnd[i]);
printf("%s\n", result_str[result]);
exit(0);
}
// remaining args are sepolicy file and context file
char *sepolicyFile = argv[index];
char *contextFile = argv[index + 1];
fp = fopen(sepolicyFile, "r");
if (!fp) {
perror(sepolicyFile);
exit(1);
}
if (sepol_set_policydb_from_file(fp) < 0) {
fprintf(stderr, "Error loading policy from %s\n", sepolicyFile);
exit(1);
}
selinux_set_callback(SELINUX_CB_VALIDATE,
(union selinux_callback)&validate);
opts[1].value = contextFile;
if (compare) {
do_compare_and_die_on_error(opts, backend, &(argv[index]));
} else {
/* remaining args are sepolicy file and context file */
char *sepolicy_file = argv[index];
char *context_file = argv[index + 1];
sehnd[0] = selabel_open(backend, opts, 2);
if (!sehnd[0]) {
fprintf(stderr, "Error loading context file from %s\n", contextFile);
exit(1);
}
if (nerr) {
fprintf(stderr, "Invalid context file found in %s\n", contextFile);
exit(1);
do_fc_check_and_die_on_error(opts, backend, mode, sepolicy_file, context_file);
}
exit(0);
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment