Add sepolicy-analyze tool.
And also remove the unnecessary references to libselinux for sepolicy-check, as it has no dependencies on libselinux. Also enable -Wall -Werror on building all of these tools and fix up all such errors. Usage: $ sepolicy-analyze -e -P out/target/product/<device>/root/sepolicy or $ sepolicy-analyze -d -P out/target/product/<device>/root/sepolicy The first form will display all type pairs that are "equivalent", i.e. they are identical with respect to allow rules, including indirect allow rules via attributes and default-enabled conditional rules (i.e. default boolean values yield a true conditional expression). Equivalent types are candidates for being coalesced into a single type. However, there may be legitimate reasons for them to remain separate, for example: - the types may differ in a respect not included in the current analysis, such as default-disabled conditional rules, audit-related rules (auditallow or dontaudit), default type transitions, or constraints (e.g. mls), or - the current policy may be overly permissive with respect to one or the other of the types and thus the correct action may be to tighten access to one or the other rather than coalescing them together, or - the domains that would in fact have different accesses to the types may not yet be defined or may be unconfined in the policy you are analyzing (e.g. in AOSP policy). The second form will display type pairs that differ and the first difference found between the two types. This output can be long. We have plans to explore further enhancements to this tool, including support for identifying isomorphic types. That will be required to identify similar domains since all domains differ in at least their entrypoint type and in their tmpfs type and thus will never show up as equivalent even if they are in all other respects identical to each other. Change-Id: If0ee00188469d2a1e165fdd52f235c705d22cd4e Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov>
This commit is contained in:
parent
a59c525ce8
commit
7b2bee99c1
4 changed files with 399 additions and 13 deletions
|
@ -5,7 +5,7 @@ include $(CLEAR_VARS)
|
|||
LOCAL_MODULE := checkseapp
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_C_INCLUDES := external/libsepol/include/
|
||||
LOCAL_CFLAGS := -DLINK_SEPOL_STATIC
|
||||
LOCAL_CFLAGS := -DLINK_SEPOL_STATIC -Wall -Werror
|
||||
LOCAL_SRC_FILES := check_seapp.c
|
||||
LOCAL_STATIC_LIBRARIES := libsepol
|
||||
|
||||
|
@ -18,6 +18,7 @@ LOCAL_MODULE := checkfc
|
|||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_C_INCLUDES := external/libsepol/include \
|
||||
external/libselinux/include
|
||||
LOCAL_CFLAGS := -Wall -Werror
|
||||
LOCAL_SRC_FILES := checkfc.c
|
||||
LOCAL_STATIC_LIBRARIES := libsepol libselinux
|
||||
|
||||
|
@ -38,9 +39,21 @@ include $(CLEAR_VARS)
|
|||
|
||||
LOCAL_MODULE := sepolicy-check
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_C_INCLUDES := external/libsepol/include \
|
||||
external/libselinux/include
|
||||
LOCAL_C_INCLUDES := external/libsepol/include
|
||||
LOCAL_CFLAGS := -Wall -Werror
|
||||
LOCAL_SRC_FILES := sepolicy-check.c
|
||||
LOCAL_STATIC_LIBRARIES := libsepol libselinux
|
||||
LOCAL_STATIC_LIBRARIES := libsepol
|
||||
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
|
||||
###################################
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := sepolicy-analyze
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_C_INCLUDES := external/libsepol/include
|
||||
LOCAL_CFLAGS := -Wall -Werror
|
||||
LOCAL_SRC_FILES := sepolicy-analyze.c
|
||||
LOCAL_STATIC_LIBRARIES := libsepol
|
||||
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
|
|
|
@ -476,7 +476,7 @@ static bool rule_map_validate(const rule_map *rm) {
|
|||
bool found_name = false;
|
||||
bool found_seinfo = false;
|
||||
char *name = NULL;
|
||||
key_map *tmp;
|
||||
const key_map *tmp;
|
||||
|
||||
for(i=0; i < rm->length; i++) {
|
||||
tmp = &(rm->m[i]);
|
||||
|
|
379
tools/sepolicy-analyze.c
Normal file
379
tools/sepolicy-analyze.c
Normal file
|
@ -0,0 +1,379 @@
|
|||
#include <getopt.h>
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <sepol/policydb/policydb.h>
|
||||
#include <sepol/policydb/services.h>
|
||||
#include <sepol/policydb/expand.h>
|
||||
#include <sepol/policydb/util.h>
|
||||
|
||||
void usage(char *arg0)
|
||||
{
|
||||
fprintf(stderr, "%s [-e|--equiv] [-d|--diff] -P <policy file>\n", arg0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int load_policy(char *filename, policydb_t * policydb, struct policy_file *pf)
|
||||
{
|
||||
int fd;
|
||||
struct stat sb;
|
||||
void *map;
|
||||
int ret;
|
||||
|
||||
fd = open(filename, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Can't open '%s': %s\n", filename, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
if (fstat(fd, &sb) < 0) {
|
||||
fprintf(stderr, "Can't stat '%s': %s\n", filename, strerror(errno));
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
map = mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
|
||||
if (map == MAP_FAILED) {
|
||||
fprintf(stderr, "Can't mmap '%s': %s\n", filename, strerror(errno));
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
policy_file_init(pf);
|
||||
pf->type = PF_USE_MEMORY;
|
||||
pf->data = map;
|
||||
pf->len = sb.st_size;
|
||||
if (policydb_init(policydb)) {
|
||||
fprintf(stderr, "Could not initialize policydb!\n");
|
||||
close(fd);
|
||||
munmap(map, sb.st_size);
|
||||
return 1;
|
||||
}
|
||||
ret = policydb_read(policydb, pf, 0);
|
||||
if (ret) {
|
||||
fprintf(stderr, "error(s) encountered while parsing configuration\n");
|
||||
close(fd);
|
||||
munmap(map, sb.st_size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int insert_type_rule(avtab_key_t * k, avtab_datum_t * d,
|
||||
struct avtab_node *type_rules)
|
||||
{
|
||||
struct avtab_node *p, *c, *n;
|
||||
|
||||
for (p = type_rules, c = type_rules->next; c; p = c, c = c->next) {
|
||||
/*
|
||||
* Find the insertion point, keeping the list
|
||||
* ordered by source type, then target type, then
|
||||
* target class.
|
||||
*/
|
||||
if (k->source_type < c->key.source_type)
|
||||
break;
|
||||
if (k->source_type == c->key.source_type &&
|
||||
k->target_type < c->key.target_type)
|
||||
break;
|
||||
if (k->source_type == c->key.source_type &&
|
||||
k->target_type == c->key.target_type &&
|
||||
k->target_class <= c->key.target_class)
|
||||
break;
|
||||
}
|
||||
|
||||
if (c &&
|
||||
k->source_type == c->key.source_type &&
|
||||
k->target_type == c->key.target_type &&
|
||||
k->target_class == c->key.target_class) {
|
||||
c->datum.data |= d->data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Insert the rule */
|
||||
n = malloc(sizeof(struct avtab_node));
|
||||
if (!n) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
n->key = *k;
|
||||
n->datum = *d;
|
||||
n->next = p->next;
|
||||
p->next = n;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int create_type_rules_helper(avtab_key_t * k, avtab_datum_t * d,
|
||||
void *args)
|
||||
{
|
||||
struct avtab_node *type_rules = args;
|
||||
avtab_key_t key;
|
||||
|
||||
/*
|
||||
* Insert the rule into the list for
|
||||
* the source type. The source type value
|
||||
* is cleared as we want to compare against other type
|
||||
* rules with different source types.
|
||||
*/
|
||||
key = *k;
|
||||
key.source_type = 0;
|
||||
if (k->source_type == k->target_type) {
|
||||
/* Clear target type as well; this is a self rule. */
|
||||
key.target_type = 0;
|
||||
}
|
||||
if (insert_type_rule(&key, d, &type_rules[k->source_type - 1]))
|
||||
return -1;
|
||||
|
||||
if (k->source_type == k->target_type)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If the target type differs, then we also
|
||||
* insert the rule into the list for the target
|
||||
* type. We clear the target type value so that
|
||||
* we can compare against other type rules with
|
||||
* different target types.
|
||||
*/
|
||||
key = *k;
|
||||
key.target_type = 0;
|
||||
if (insert_type_rule(&key, d, &type_rules[k->target_type - 1]))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int create_type_rules(avtab_key_t * k, avtab_datum_t * d, void *args)
|
||||
{
|
||||
if (k->specified & AVTAB_ALLOWED)
|
||||
return create_type_rules_helper(k, d, args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int create_type_rules_cond(avtab_key_t * k, avtab_datum_t * d,
|
||||
void *args)
|
||||
{
|
||||
if ((k->specified & (AVTAB_ALLOWED|AVTAB_ENABLED)) ==
|
||||
(AVTAB_ALLOWED|AVTAB_ENABLED))
|
||||
return create_type_rules_helper(k, d, args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void free_type_rules(struct avtab_node *l)
|
||||
{
|
||||
struct avtab_node *tmp;
|
||||
|
||||
while (l) {
|
||||
tmp = l;
|
||||
l = l->next;
|
||||
free(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
static void display_allow(policydb_t *policydb, struct avtab_node *n, int idx,
|
||||
uint32_t perms)
|
||||
{
|
||||
printf(" allow %s %s:%s { %s };\n",
|
||||
policydb->p_type_val_to_name[n->key.source_type
|
||||
? n->key.source_type - 1 : idx],
|
||||
n->key.target_type == n->key.source_type ? "self" :
|
||||
policydb->p_type_val_to_name[n->key.target_type
|
||||
? n->key.target_type - 1 : idx],
|
||||
policydb->p_class_val_to_name[n->key.target_class - 1],
|
||||
sepol_av_to_string
|
||||
(policydb, n->key.target_class, perms));
|
||||
}
|
||||
|
||||
static int find_match(policydb_t *policydb, struct avtab_node *l1,
|
||||
int idx1, struct avtab_node *l2, int idx2)
|
||||
{
|
||||
struct avtab_node *c;
|
||||
uint32_t perms1, perms2;
|
||||
|
||||
for (c = l2; c; c = c->next) {
|
||||
if (l1->key.source_type < c->key.source_type)
|
||||
break;
|
||||
if (l1->key.source_type == c->key.source_type &&
|
||||
l1->key.target_type < c->key.target_type)
|
||||
break;
|
||||
if (l1->key.source_type == c->key.source_type &&
|
||||
l1->key.target_type == c->key.target_type &&
|
||||
l1->key.target_class <= c->key.target_class)
|
||||
break;
|
||||
}
|
||||
|
||||
if (c &&
|
||||
l1->key.source_type == c->key.source_type &&
|
||||
l1->key.target_type == c->key.target_type &&
|
||||
l1->key.target_class == c->key.target_class) {
|
||||
perms1 = l1->datum.data & ~c->datum.data;
|
||||
perms2 = c->datum.data & ~l1->datum.data;
|
||||
if (perms1 || perms2) {
|
||||
if (perms1)
|
||||
display_allow(policydb, l1, idx1, perms1);
|
||||
if (perms2)
|
||||
display_allow(policydb, c, idx2, perms2);
|
||||
printf("\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int analyze_types(policydb_t * policydb, char equiv, char diff)
|
||||
{
|
||||
avtab_t exp_avtab, exp_cond_avtab;
|
||||
struct avtab_node *type_rules, *l1, *l2;
|
||||
struct type_datum *type;
|
||||
size_t i, j;
|
||||
|
||||
/*
|
||||
* Create a list of access vector rules for each type
|
||||
* from the access vector table.
|
||||
*/
|
||||
type_rules = malloc(sizeof(struct avtab_node) * policydb->p_types.nprim);
|
||||
if (!type_rules) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
memset(type_rules, 0, sizeof(struct avtab_node) * policydb->p_types.nprim);
|
||||
|
||||
if (avtab_init(&exp_avtab) || avtab_init(&exp_cond_avtab)) {
|
||||
fputs("out of memory\n", stderr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (expand_avtab(policydb, &policydb->te_avtab, &exp_avtab)) {
|
||||
fputs("out of memory\n", stderr);
|
||||
avtab_destroy(&exp_avtab);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (expand_avtab(policydb, &policydb->te_cond_avtab, &exp_cond_avtab)) {
|
||||
fputs("out of memory\n", stderr);
|
||||
avtab_destroy(&exp_avtab);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (avtab_map(&exp_avtab, create_type_rules, type_rules))
|
||||
exit(1);
|
||||
|
||||
if (avtab_map(&exp_cond_avtab, create_type_rules_cond, type_rules))
|
||||
exit(1);
|
||||
|
||||
avtab_destroy(&exp_avtab);
|
||||
avtab_destroy(&exp_cond_avtab);
|
||||
|
||||
/*
|
||||
* Compare the type lists and identify similar types.
|
||||
*/
|
||||
for (i = 0; i < policydb->p_types.nprim - 1; i++) {
|
||||
if (!type_rules[i].next)
|
||||
continue;
|
||||
type = policydb->type_val_to_struct[i];
|
||||
if (type->flavor) {
|
||||
free_type_rules(type_rules[i].next);
|
||||
type_rules[i].next = NULL;
|
||||
continue;
|
||||
}
|
||||
for (j = i + 1; j < policydb->p_types.nprim; j++) {
|
||||
type = policydb->type_val_to_struct[j];
|
||||
if (type->flavor) {
|
||||
free_type_rules(type_rules[j].next);
|
||||
type_rules[j].next = NULL;
|
||||
continue;
|
||||
}
|
||||
for (l1 = type_rules[i].next, l2 = type_rules[j].next;
|
||||
l1 && l2; l1 = l1->next, l2 = l2->next) {
|
||||
if (l1->key.source_type != l2->key.source_type)
|
||||
break;
|
||||
if (l1->key.target_type != l2->key.target_type)
|
||||
break;
|
||||
if (l1->key.target_class != l2->key.target_class
|
||||
|| l1->datum.data != l2->datum.data)
|
||||
break;
|
||||
}
|
||||
if (l1 || l2) {
|
||||
if (diff) {
|
||||
printf
|
||||
("Types %s and %s differ, starting with:\n",
|
||||
policydb->p_type_val_to_name[i],
|
||||
policydb->p_type_val_to_name[j]);
|
||||
|
||||
if (l1 && l2) {
|
||||
if (find_match(policydb, l1, i, l2, j))
|
||||
continue;
|
||||
if (find_match(policydb, l2, j, l1, i))
|
||||
continue;
|
||||
}
|
||||
if (l1)
|
||||
display_allow(policydb, l1, i, l1->datum.data);
|
||||
if (l2)
|
||||
display_allow(policydb, l2, j, l2->datum.data);
|
||||
printf("\n");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
free_type_rules(type_rules[j].next);
|
||||
type_rules[j].next = NULL;
|
||||
if (equiv) {
|
||||
printf("Types %s and %s are equivalent.\n",
|
||||
policydb->p_type_val_to_name[i],
|
||||
policydb->p_type_val_to_name[j]);
|
||||
}
|
||||
}
|
||||
free_type_rules(type_rules[i].next);
|
||||
type_rules[i].next = NULL;
|
||||
}
|
||||
|
||||
free(type_rules);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char *policy = NULL;
|
||||
struct policy_file pf;
|
||||
policydb_t policydb;
|
||||
char ch;
|
||||
char equiv = 0, diff = 0;
|
||||
|
||||
struct option long_options[] = {
|
||||
{"equiv", no_argument, NULL, 'e'},
|
||||
{"diff", no_argument, NULL, 'd'},
|
||||
{"policy", required_argument, NULL, 'P'},
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
|
||||
while ((ch = getopt_long(argc, argv, "edP:", long_options, NULL)) != -1) {
|
||||
switch (ch) {
|
||||
case 'e':
|
||||
equiv = 1;
|
||||
break;
|
||||
case 'd':
|
||||
diff = 1;
|
||||
break;
|
||||
case 'P':
|
||||
policy = optarg;
|
||||
break;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!policy || (!equiv && !diff))
|
||||
usage(argv[0]);
|
||||
|
||||
if (load_policy(policy, &policydb, &pf))
|
||||
exit(1);
|
||||
|
||||
analyze_types(&policydb, equiv, diff);
|
||||
|
||||
policydb_destroy(&policydb);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,11 +1,3 @@
|
|||
/*
|
||||
* This was derived from public domain works with updates to
|
||||
* work with more modern SELinux libraries.
|
||||
*
|
||||
* It is released into the public domain.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <getopt.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -133,6 +125,8 @@ int check_rule(char *s, char *t, char *c, char *p, policydb_t *policy) {
|
|||
avtab_key_t key;
|
||||
int match;
|
||||
|
||||
key.source_type = key.target_type = key.target_class = 0;
|
||||
|
||||
if (s_op != ANY) {
|
||||
src = hashtab_search(policy->p_types.table, s);
|
||||
if (src == NULL) {
|
||||
|
|
Loading…
Reference in a new issue