Accept command-line input for neverallow-check.
Also, divide each sepolicy-analyze function into its own component for simplified command-line parsing and potentially eventual modularization. Bug: 18005561 Cherry-pick from: https://android-review.googlesource.com/#/c/111626/ Change-Id: I751a99feffe820308ec58514fdba4cdef184d964
This commit is contained in:
parent
db4582a996
commit
3fa92beda7
16 changed files with 1217 additions and 1031 deletions
|
@ -46,14 +46,4 @@ LOCAL_STATIC_LIBRARIES := libsepol
|
||||||
|
|
||||||
include $(BUILD_HOST_EXECUTABLE)
|
include $(BUILD_HOST_EXECUTABLE)
|
||||||
|
|
||||||
###################################
|
include $(call all-makefiles-under,$(LOCAL_PATH))
|
||||||
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)
|
|
79
tools/README
79
tools/README
|
@ -50,81 +50,4 @@ sepolicy-check
|
||||||
|
|
||||||
sepolicy-analyze
|
sepolicy-analyze
|
||||||
A tool for performing various kinds of analysis on a sepolicy
|
A tool for performing various kinds of analysis on a sepolicy
|
||||||
file. The current kinds of analysis that are currently supported
|
file.
|
||||||
include:
|
|
||||||
|
|
||||||
TYPE EQUIVALENCE
|
|
||||||
sepolicy-analyze -e -P out/target/product/<board>/root/sepolicy
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
TYPE DIFFERENCE
|
|
||||||
sepolicy-analyze -d -P out/target/product/<board>/root/sepolicy
|
|
||||||
|
|
||||||
Display type pairs that differ and the first difference found
|
|
||||||
between the two types. This may be used in looking for similar
|
|
||||||
types that are not equivalent but may be candidates for coalescing.
|
|
||||||
|
|
||||||
DUPLICATE ALLOW RULES
|
|
||||||
sepolicy-analyze -D -P out/target/product/<board>/root/sepolicy
|
|
||||||
|
|
||||||
Displays duplicate allow rules, i.e. pairs of allow rules that
|
|
||||||
grant the same permissions where one allow rule is written
|
|
||||||
directly in terms of individual types and the other is written in
|
|
||||||
terms of attributes associated with those same types. The rule
|
|
||||||
with individual types is a candidate for removal. The rule with
|
|
||||||
individual types may be directly represented in the source policy
|
|
||||||
or may be a result of expansion of a type negation (e.g. domain
|
|
||||||
-foo -bar is expanded to individual allow rules by the policy
|
|
||||||
compiler). Domains with unconfineddomain will typically have such
|
|
||||||
duplicate rules as a natural side effect and can be ignored.
|
|
||||||
|
|
||||||
PERMISSIVE DOMAINS
|
|
||||||
sepolicy-analyze -p -P out/target/product/<board>/root/sepolicy
|
|
||||||
|
|
||||||
Displays domains in the policy that are permissive, i.e. avc
|
|
||||||
denials are logged but not enforced for these domains. While
|
|
||||||
permissive domains can be helpful during development, they
|
|
||||||
should not be present in a final -user build.
|
|
||||||
|
|
||||||
NEVERALLOW CHECKING
|
|
||||||
sepolicy-analyze [-w] [-z] -n neverallows.conf -P out/target/product/<board>/root/sepolicy
|
|
||||||
|
|
||||||
Check whether the sepolicy file violates any of the neverallow rules
|
|
||||||
from neverallows.conf. neverallows.conf is a file containing neverallow
|
|
||||||
statements in the same format as the SELinux policy.conf file, i.e. after
|
|
||||||
m4 macro expansion of the rules from a .te file. You can use an entire
|
|
||||||
policy.conf file as the neverallows.conf file and sepolicy-analyze will
|
|
||||||
ignore everything except for the neverallows within it. If there are
|
|
||||||
no violations, sepolicy-analyze will exit successfully with no output.
|
|
||||||
Otherwise, sepolicy-analyze will report all violations and exit
|
|
||||||
with a non-zero exit status.
|
|
||||||
|
|
||||||
The -w or --warn option may be used to warn on any types, attributes,
|
|
||||||
classes, or permissions from a neverallow rule that could not be resolved
|
|
||||||
within the sepolicy file. This can be normal due to differences between
|
|
||||||
the policy from which the neverallow rules were taken and the policy
|
|
||||||
being checked. Such values are ignored for the purposes of neverallow
|
|
||||||
checking.
|
|
||||||
|
|
||||||
The -z (-d was already taken!) or --debug option may be used to cause
|
|
||||||
sepolicy-analyze to emit the neverallow rules as it parses them from
|
|
||||||
the neverallows.conf file. This is principally a debugging facility
|
|
||||||
for the parser but could also be used to extract neverallow rules from
|
|
||||||
a full policy.conf file and output them in a more easily parsed format.
|
|
|
@ -1,942 +0,0 @@
|
||||||
#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>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
static int debug;
|
|
||||||
static int warn;
|
|
||||||
|
|
||||||
void usage(char *arg0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "%s [-w|--warn] [-z|--debug] [-e|--equiv] [-d|--diff] [-D|--dups] [-p|--permissive] [-n|--neverallow <neverallow file>] -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, avtab_key_t *key, int idx,
|
|
||||||
uint32_t perms)
|
|
||||||
{
|
|
||||||
printf(" allow %s %s:%s { %s };\n",
|
|
||||||
policydb->p_type_val_to_name[key->source_type
|
|
||||||
? key->source_type - 1 : idx],
|
|
||||||
key->target_type == key->source_type ? "self" :
|
|
||||||
policydb->p_type_val_to_name[key->target_type
|
|
||||||
? key->target_type - 1 : idx],
|
|
||||||
policydb->p_class_val_to_name[key->target_class - 1],
|
|
||||||
sepol_av_to_string
|
|
||||||
(policydb, 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->key, idx1, perms1);
|
|
||||||
if (perms2)
|
|
||||||
display_allow(policydb, &c->key, 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->key, i, l1->datum.data);
|
|
||||||
if (l2)
|
|
||||||
display_allow(policydb, &l2->key, 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int find_dups_helper(avtab_key_t * k, avtab_datum_t * d,
|
|
||||||
void *args)
|
|
||||||
{
|
|
||||||
policydb_t *policydb = args;
|
|
||||||
ebitmap_t *sattr, *tattr;
|
|
||||||
ebitmap_node_t *snode, *tnode;
|
|
||||||
unsigned int i, j;
|
|
||||||
avtab_key_t avkey;
|
|
||||||
avtab_ptr_t node;
|
|
||||||
struct type_datum *stype, *ttype, *stype2, *ttype2;
|
|
||||||
bool attrib1, attrib2;
|
|
||||||
|
|
||||||
if (!(k->specified & AVTAB_ALLOWED))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (k->source_type == k->target_type)
|
|
||||||
return 0; /* self rule */
|
|
||||||
|
|
||||||
avkey.target_class = k->target_class;
|
|
||||||
avkey.specified = k->specified;
|
|
||||||
|
|
||||||
sattr = &policydb->type_attr_map[k->source_type - 1];
|
|
||||||
tattr = &policydb->type_attr_map[k->target_type - 1];
|
|
||||||
stype = policydb->type_val_to_struct[k->source_type - 1];
|
|
||||||
ttype = policydb->type_val_to_struct[k->target_type - 1];
|
|
||||||
attrib1 = stype->flavor || ttype->flavor;
|
|
||||||
ebitmap_for_each_bit(sattr, snode, i) {
|
|
||||||
if (!ebitmap_node_get_bit(snode, i))
|
|
||||||
continue;
|
|
||||||
ebitmap_for_each_bit(tattr, tnode, j) {
|
|
||||||
if (!ebitmap_node_get_bit(tnode, j))
|
|
||||||
continue;
|
|
||||||
avkey.source_type = i + 1;
|
|
||||||
avkey.target_type = j + 1;
|
|
||||||
if (avkey.source_type == k->source_type &&
|
|
||||||
avkey.target_type == k->target_type)
|
|
||||||
continue;
|
|
||||||
if (avkey.source_type == avkey.target_type)
|
|
||||||
continue; /* self rule */
|
|
||||||
stype2 = policydb->type_val_to_struct[avkey.source_type - 1];
|
|
||||||
ttype2 = policydb->type_val_to_struct[avkey.target_type - 1];
|
|
||||||
attrib2 = stype2->flavor || ttype2->flavor;
|
|
||||||
if (attrib1 && attrib2)
|
|
||||||
continue; /* overlapping attribute-based rules */
|
|
||||||
for (node = avtab_search_node(&policydb->te_avtab, &avkey);
|
|
||||||
node != NULL;
|
|
||||||
node = avtab_search_node_next(node, avkey.specified)) {
|
|
||||||
uint32_t perms = node->datum.data & d->data;
|
|
||||||
if ((attrib1 && perms == node->datum.data) ||
|
|
||||||
(attrib2 && perms == d->data)) {
|
|
||||||
/*
|
|
||||||
* The attribute-based rule is a superset of the
|
|
||||||
* non-attribute-based rule. This is a dup.
|
|
||||||
*/
|
|
||||||
printf("Duplicate allow rule found:\n");
|
|
||||||
display_allow(policydb, k, i, d->data);
|
|
||||||
display_allow(policydb, &node->key, i, node->datum.data);
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int find_dups(policydb_t * policydb)
|
|
||||||
{
|
|
||||||
if (avtab_map(&policydb->te_avtab, find_dups_helper, policydb))
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int list_permissive(policydb_t * policydb)
|
|
||||||
{
|
|
||||||
struct ebitmap_node *n;
|
|
||||||
unsigned int bit;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* iterate over all domains and check if domain is in permissive
|
|
||||||
*/
|
|
||||||
ebitmap_for_each_bit(&policydb->permissive_map, n, bit)
|
|
||||||
{
|
|
||||||
if (ebitmap_node_get_bit(n, bit)) {
|
|
||||||
printf("%s\n", policydb->p_type_val_to_name[bit -1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int read_typeset(policydb_t *policydb, char **ptr, char *end,
|
|
||||||
type_set_t *typeset, uint32_t *flags)
|
|
||||||
{
|
|
||||||
const char *keyword = "self";
|
|
||||||
size_t keyword_size = strlen(keyword), len;
|
|
||||||
char *p = *ptr;
|
|
||||||
unsigned openparens = 0;
|
|
||||||
char *start, *id;
|
|
||||||
type_datum_t *type;
|
|
||||||
struct ebitmap_node *n;
|
|
||||||
unsigned int bit;
|
|
||||||
bool negate = false;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
do {
|
|
||||||
while (p < end && isspace(*p))
|
|
||||||
p++;
|
|
||||||
|
|
||||||
if (p == end)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
if (*p == '~') {
|
|
||||||
if (debug)
|
|
||||||
printf(" ~");
|
|
||||||
typeset->flags = TYPE_COMP;
|
|
||||||
p++;
|
|
||||||
while (p < end && isspace(*p))
|
|
||||||
p++;
|
|
||||||
if (p == end)
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*p == '{') {
|
|
||||||
if (debug && !openparens)
|
|
||||||
printf(" {");
|
|
||||||
openparens++;
|
|
||||||
p++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*p == '}') {
|
|
||||||
if (debug && openparens == 1)
|
|
||||||
printf(" }");
|
|
||||||
if (openparens == 0)
|
|
||||||
goto err;
|
|
||||||
openparens--;
|
|
||||||
p++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*p == '*') {
|
|
||||||
if (debug)
|
|
||||||
printf(" *");
|
|
||||||
typeset->flags = TYPE_STAR;
|
|
||||||
p++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*p == '-') {
|
|
||||||
if (debug)
|
|
||||||
printf(" -");
|
|
||||||
negate = true;
|
|
||||||
p++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*p == '#') {
|
|
||||||
while (p < end && *p != '\n')
|
|
||||||
p++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
start = p;
|
|
||||||
while (p < end && !isspace(*p) && *p != ':' && *p != ';' && *p != '{' && *p != '}' && *p != '#')
|
|
||||||
p++;
|
|
||||||
|
|
||||||
if (p == start)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
len = p - start;
|
|
||||||
if (len == keyword_size && !strncmp(start, keyword, keyword_size)) {
|
|
||||||
if (debug)
|
|
||||||
printf(" self");
|
|
||||||
*flags |= RULE_SELF;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
id = calloc(1, len + 1);
|
|
||||||
if (!id)
|
|
||||||
goto err;
|
|
||||||
memcpy(id, start, len);
|
|
||||||
if (debug)
|
|
||||||
printf(" %s", id);
|
|
||||||
type = hashtab_search(policydb->p_types.table, id);
|
|
||||||
if (!type) {
|
|
||||||
if (warn)
|
|
||||||
fprintf(stderr, "Warning! Type or attribute %s used in neverallow undefined in policy being checked.\n", id);
|
|
||||||
negate = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
free(id);
|
|
||||||
|
|
||||||
if (type->flavor == TYPE_ATTRIB) {
|
|
||||||
if (negate)
|
|
||||||
rc = ebitmap_union(&typeset->negset, &policydb->attr_type_map[type->s.value - 1]);
|
|
||||||
else
|
|
||||||
rc = ebitmap_union(&typeset->types, &policydb->attr_type_map[type->s.value - 1]);
|
|
||||||
} else if (negate) {
|
|
||||||
rc = ebitmap_set_bit(&typeset->negset, type->s.value - 1, 1);
|
|
||||||
} else {
|
|
||||||
rc = ebitmap_set_bit(&typeset->types, type->s.value - 1, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
negate = false;
|
|
||||||
|
|
||||||
if (rc)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
} while (p < end && openparens);
|
|
||||||
|
|
||||||
if (p == end)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
if (typeset->flags & TYPE_STAR) {
|
|
||||||
for (bit = 0; bit < policydb->p_types.nprim; bit++) {
|
|
||||||
if (ebitmap_get_bit(&typeset->negset, bit))
|
|
||||||
continue;
|
|
||||||
if (policydb->type_val_to_struct[bit] &&
|
|
||||||
policydb->type_val_to_struct[bit]->flavor == TYPE_ATTRIB)
|
|
||||||
continue;
|
|
||||||
if (ebitmap_set_bit(&typeset->types, bit, 1))
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ebitmap_for_each_bit(&typeset->negset, n, bit) {
|
|
||||||
if (!ebitmap_node_get_bit(n, bit))
|
|
||||||
continue;
|
|
||||||
if (ebitmap_set_bit(&typeset->types, bit, 0))
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeset->flags & TYPE_COMP) {
|
|
||||||
for (bit = 0; bit < policydb->p_types.nprim; bit++) {
|
|
||||||
if (policydb->type_val_to_struct[bit] &&
|
|
||||||
policydb->type_val_to_struct[bit]->flavor == TYPE_ATTRIB)
|
|
||||||
continue;
|
|
||||||
if (ebitmap_get_bit(&typeset->types, bit))
|
|
||||||
ebitmap_set_bit(&typeset->types, bit, 0);
|
|
||||||
else {
|
|
||||||
if (ebitmap_set_bit(&typeset->types, bit, 1))
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (warn && ebitmap_length(&typeset->types) == 0 && !(*flags))
|
|
||||||
fprintf(stderr, "Warning! Empty type set\n");
|
|
||||||
|
|
||||||
*ptr = p;
|
|
||||||
return 0;
|
|
||||||
err:
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int read_classperms(policydb_t *policydb, char **ptr, char *end,
|
|
||||||
class_perm_node_t **perms)
|
|
||||||
{
|
|
||||||
char *p = *ptr;
|
|
||||||
unsigned openparens = 0;
|
|
||||||
char *id, *start;
|
|
||||||
class_datum_t *cls = NULL;
|
|
||||||
perm_datum_t *perm = NULL;
|
|
||||||
class_perm_node_t *classperms = NULL, *node = NULL;
|
|
||||||
bool complement = false;
|
|
||||||
|
|
||||||
while (p < end && isspace(*p))
|
|
||||||
p++;
|
|
||||||
|
|
||||||
if (p == end || *p != ':')
|
|
||||||
goto err;
|
|
||||||
p++;
|
|
||||||
|
|
||||||
if (debug)
|
|
||||||
printf(" :");
|
|
||||||
|
|
||||||
do {
|
|
||||||
while (p < end && isspace(*p))
|
|
||||||
p++;
|
|
||||||
|
|
||||||
if (p == end)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
if (*p == '{') {
|
|
||||||
if (debug && !openparens)
|
|
||||||
printf(" {");
|
|
||||||
openparens++;
|
|
||||||
p++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*p == '}') {
|
|
||||||
if (debug && openparens == 1)
|
|
||||||
printf(" }");
|
|
||||||
if (openparens == 0)
|
|
||||||
goto err;
|
|
||||||
openparens--;
|
|
||||||
p++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*p == '#') {
|
|
||||||
while (p < end && *p != '\n')
|
|
||||||
p++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
start = p;
|
|
||||||
while (p < end && !isspace(*p) && *p != '{' && *p != '}' && *p != ';' && *p != '#')
|
|
||||||
p++;
|
|
||||||
|
|
||||||
if (p == start)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
id = calloc(1, p - start + 1);
|
|
||||||
if (!id)
|
|
||||||
goto err;
|
|
||||||
memcpy(id, start, p - start);
|
|
||||||
if (debug)
|
|
||||||
printf(" %s", id);
|
|
||||||
cls = hashtab_search(policydb->p_classes.table, id);
|
|
||||||
if (!cls) {
|
|
||||||
if (warn)
|
|
||||||
fprintf(stderr, "Warning! Class %s used in neverallow undefined in policy being checked.\n", id);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
node = calloc(1, sizeof *node);
|
|
||||||
if (!node)
|
|
||||||
goto err;
|
|
||||||
node->class = cls->s.value;
|
|
||||||
node->next = classperms;
|
|
||||||
classperms = node;
|
|
||||||
free(id);
|
|
||||||
} while (p < end && openparens);
|
|
||||||
|
|
||||||
if (p == end)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
if (warn && !classperms)
|
|
||||||
fprintf(stderr, "Warning! Empty class set\n");
|
|
||||||
|
|
||||||
do {
|
|
||||||
while (p < end && isspace(*p))
|
|
||||||
p++;
|
|
||||||
|
|
||||||
if (p == end)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
if (*p == '~') {
|
|
||||||
if (debug)
|
|
||||||
printf(" ~");
|
|
||||||
complement = true;
|
|
||||||
p++;
|
|
||||||
while (p < end && isspace(*p))
|
|
||||||
p++;
|
|
||||||
if (p == end)
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*p == '{') {
|
|
||||||
if (debug && !openparens)
|
|
||||||
printf(" {");
|
|
||||||
openparens++;
|
|
||||||
p++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*p == '}') {
|
|
||||||
if (debug && openparens == 1)
|
|
||||||
printf(" }");
|
|
||||||
if (openparens == 0)
|
|
||||||
goto err;
|
|
||||||
openparens--;
|
|
||||||
p++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*p == '#') {
|
|
||||||
while (p < end && *p != '\n')
|
|
||||||
p++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
start = p;
|
|
||||||
while (p < end && !isspace(*p) && *p != '{' && *p != '}' && *p != ';' && *p != '#')
|
|
||||||
p++;
|
|
||||||
|
|
||||||
if (p == start)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
id = calloc(1, p - start + 1);
|
|
||||||
if (!id)
|
|
||||||
goto err;
|
|
||||||
memcpy(id, start, p - start);
|
|
||||||
if (debug)
|
|
||||||
printf(" %s", id);
|
|
||||||
|
|
||||||
if (!strcmp(id, "*")) {
|
|
||||||
for (node = classperms; node; node = node->next)
|
|
||||||
node->data = ~0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (node = classperms; node; node = node->next) {
|
|
||||||
cls = policydb->class_val_to_struct[node->class-1];
|
|
||||||
perm = hashtab_search(cls->permissions.table, id);
|
|
||||||
if (cls->comdatum && !perm)
|
|
||||||
perm = hashtab_search(cls->comdatum->permissions.table, id);
|
|
||||||
if (!perm) {
|
|
||||||
if (warn)
|
|
||||||
fprintf(stderr, "Warning! Permission %s used in neverallow undefined in class %s in policy being checked.\n", id, policydb->p_class_val_to_name[node->class-1]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
node->data |= 1U << (perm->s.value - 1);
|
|
||||||
}
|
|
||||||
free(id);
|
|
||||||
} while (p < end && openparens);
|
|
||||||
|
|
||||||
if (p == end)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
if (complement) {
|
|
||||||
for (node = classperms; node; node = node->next)
|
|
||||||
node->data = ~node->data;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (warn) {
|
|
||||||
for (node = classperms; node; node = node->next)
|
|
||||||
if (!node->data)
|
|
||||||
fprintf(stderr, "Warning! Empty permission set\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
*perms = classperms;
|
|
||||||
*ptr = p;
|
|
||||||
return 0;
|
|
||||||
err:
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int check_neverallows(policydb_t *policydb, const char *filename)
|
|
||||||
{
|
|
||||||
const char *keyword = "neverallow";
|
|
||||||
size_t keyword_size = strlen(keyword), len;
|
|
||||||
struct avrule *neverallows = NULL, *avrule;
|
|
||||||
int fd;
|
|
||||||
struct stat sb;
|
|
||||||
char *text, *end, *start;
|
|
||||||
char *p;
|
|
||||||
|
|
||||||
fd = open(filename, O_RDONLY);
|
|
||||||
if (fd < 0) {
|
|
||||||
fprintf(stderr, "Could not 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;
|
|
||||||
}
|
|
||||||
text = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
|
||||||
end = text + sb.st_size;
|
|
||||||
if (text == MAP_FAILED) {
|
|
||||||
fprintf(stderr, "Can't mmap '%s': %s\n", filename, strerror(errno));
|
|
||||||
close(fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
close(fd);
|
|
||||||
|
|
||||||
p = text;
|
|
||||||
while (p < end) {
|
|
||||||
while (p < end && isspace(*p))
|
|
||||||
p++;
|
|
||||||
|
|
||||||
if (*p == '#') {
|
|
||||||
while (p < end && *p != '\n')
|
|
||||||
p++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
start = p;
|
|
||||||
while (p < end && !isspace(*p))
|
|
||||||
p++;
|
|
||||||
|
|
||||||
len = p - start;
|
|
||||||
if (len != keyword_size || strncmp(start, keyword, keyword_size))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (debug)
|
|
||||||
printf("neverallow");
|
|
||||||
|
|
||||||
avrule = calloc(1, sizeof *avrule);
|
|
||||||
if (!avrule)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
avrule->specified = AVRULE_NEVERALLOW;
|
|
||||||
|
|
||||||
if (read_typeset(policydb, &p, end, &avrule->stypes, &avrule->flags))
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
if (read_typeset(policydb, &p, end, &avrule->ttypes, &avrule->flags))
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
if (read_classperms(policydb, &p, end, &avrule->perms))
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
while (p < end && *p != ';')
|
|
||||||
p++;
|
|
||||||
|
|
||||||
if (p == end || *p != ';')
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
if (debug)
|
|
||||||
printf(";\n");
|
|
||||||
|
|
||||||
avrule->next = neverallows;
|
|
||||||
neverallows = avrule;
|
|
||||||
}
|
|
||||||
|
|
||||||
return check_assertions(NULL, policydb, neverallows);
|
|
||||||
err:
|
|
||||||
if (errno == ENOMEM) {
|
|
||||||
fprintf(stderr, "Out of memory while parsing %s\n", filename);
|
|
||||||
} else
|
|
||||||
fprintf(stderr, "Error while parsing %s\n", filename);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
char *policy = NULL, *neverallows = NULL;
|
|
||||||
struct policy_file pf;
|
|
||||||
policydb_t policydb;
|
|
||||||
char ch;
|
|
||||||
char equiv = 0, diff = 0, dups = 0, permissive = 0;
|
|
||||||
int rc = 0;
|
|
||||||
|
|
||||||
struct option long_options[] = {
|
|
||||||
{"equiv", no_argument, NULL, 'e'},
|
|
||||||
{"debug", no_argument, NULL, 'z'},
|
|
||||||
{"diff", no_argument, NULL, 'd'},
|
|
||||||
{"dups", no_argument, NULL, 'D'},
|
|
||||||
{"neverallow", required_argument, NULL, 'n'},
|
|
||||||
{"permissive", no_argument, NULL, 'p'},
|
|
||||||
{"policy", required_argument, NULL, 'P'},
|
|
||||||
{"warn", no_argument, NULL, 'w'},
|
|
||||||
{NULL, 0, NULL, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
while ((ch = getopt_long(argc, argv, "edDpn:P:wz", long_options, NULL)) != -1) {
|
|
||||||
switch (ch) {
|
|
||||||
case 'e':
|
|
||||||
equiv = 1;
|
|
||||||
break;
|
|
||||||
case 'd':
|
|
||||||
diff = 1;
|
|
||||||
break;
|
|
||||||
case 'D':
|
|
||||||
dups = 1;
|
|
||||||
break;
|
|
||||||
case 'n':
|
|
||||||
neverallows = optarg;
|
|
||||||
break;
|
|
||||||
case 'p':
|
|
||||||
permissive = 1;
|
|
||||||
break;
|
|
||||||
case 'P':
|
|
||||||
policy = optarg;
|
|
||||||
break;
|
|
||||||
case 'w':
|
|
||||||
warn = 1;
|
|
||||||
break;
|
|
||||||
case 'z':
|
|
||||||
debug = 1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
usage(argv[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!policy || (!equiv && !diff && !dups && !permissive && !neverallows))
|
|
||||||
usage(argv[0]);
|
|
||||||
|
|
||||||
if (load_policy(policy, &policydb, &pf))
|
|
||||||
exit(1);
|
|
||||||
|
|
||||||
if (equiv || diff)
|
|
||||||
analyze_types(&policydb, equiv, diff);
|
|
||||||
|
|
||||||
if (dups)
|
|
||||||
find_dups(&policydb);
|
|
||||||
|
|
||||||
if (permissive)
|
|
||||||
list_permissive(&policydb);
|
|
||||||
|
|
||||||
if (neverallows)
|
|
||||||
rc |= check_neverallows(&policydb, neverallows);
|
|
||||||
|
|
||||||
policydb_destroy(&policydb);
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
13
tools/sepolicy-analyze/Android.mk
Normal file
13
tools/sepolicy-analyze/Android.mk
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
LOCAL_PATH:= $(call my-dir)
|
||||||
|
|
||||||
|
###################################
|
||||||
|
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 dups.c neverallow.c perm.c typecmp.c utils.c
|
||||||
|
LOCAL_STATIC_LIBRARIES := libsepol
|
||||||
|
|
||||||
|
include $(BUILD_HOST_EXECUTABLE)
|
82
tools/sepolicy-analyze/README
Normal file
82
tools/sepolicy-analyze/README
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
sepolicy-analyze
|
||||||
|
A component-ized tool for performing various kinds of analysis on a
|
||||||
|
sepolicy file. The current kinds of analysis that are currently
|
||||||
|
supported include:
|
||||||
|
|
||||||
|
TYPE EQUIVALENCE (typecmp)
|
||||||
|
sepolicy-analyze out/target/product/<board>/root/sepolicy typecmp -e
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
TYPE DIFFERENCE (typecmp)
|
||||||
|
sepolicy-analyze out/target/product/<board>/root/sepolicy typecmp -d
|
||||||
|
|
||||||
|
Display type pairs that differ and the first difference found
|
||||||
|
between the two types. This may be used in looking for similar
|
||||||
|
types that are not equivalent but may be candidates for coalescing.
|
||||||
|
|
||||||
|
DUPLICATE ALLOW RULES (dups)
|
||||||
|
sepolicy-analyze out/target/product/<board>/root/sepolicy dups
|
||||||
|
|
||||||
|
Displays duplicate allow rules, i.e. pairs of allow rules that
|
||||||
|
grant the same permissions where one allow rule is written
|
||||||
|
directly in terms of individual types and the other is written in
|
||||||
|
terms of attributes associated with those same types. The rule
|
||||||
|
with individual types is a candidate for removal. The rule with
|
||||||
|
individual types may be directly represented in the source policy
|
||||||
|
or may be a result of expansion of a type negation (e.g. domain
|
||||||
|
-foo -bar is expanded to individual allow rules by the policy
|
||||||
|
compiler). Domains with unconfineddomain will typically have such
|
||||||
|
duplicate rules as a natural side effect and can be ignored.
|
||||||
|
|
||||||
|
PERMISSIVE DOMAINS (permissive)
|
||||||
|
sepolicy-analyze out/target/product/<board>/root/sepolicy permissive
|
||||||
|
|
||||||
|
Displays domains in the policy that are permissive, i.e. avc
|
||||||
|
denials are logged but not enforced for these domains. While
|
||||||
|
permissive domains can be helpful during development, they
|
||||||
|
should not be present in a final -user build.
|
||||||
|
|
||||||
|
NEVERALLOW CHECKING (neverallow)
|
||||||
|
sepolicy-analyze out/target/product/<board>/root/sepolicy neverallow \
|
||||||
|
[-w] [-d] [-f neverallows.conf] | [-n "neverallow string"]
|
||||||
|
|
||||||
|
Check whether the sepolicy file violates any of the neverallow rules
|
||||||
|
from the neverallows.conf file or a given string, which contain neverallow
|
||||||
|
statements in the same format as the SELinux policy.conf file, i.e. after
|
||||||
|
m4 macro expansion of the rules from a .te file. You can use an entire
|
||||||
|
policy.conf file as the neverallows.conf file and sepolicy-analyze will
|
||||||
|
ignore everything except for the neverallows within it. You can also
|
||||||
|
specify this as a command-line string argument, which could be useful for
|
||||||
|
quickly checking an individual expanded rule or group of rules. If there are
|
||||||
|
no violations, sepolicy-analyze will exit successfully with no output.
|
||||||
|
Otherwise, sepolicy-analyze will report all violations and exit
|
||||||
|
with a non-zero exit status.
|
||||||
|
|
||||||
|
The -w or --warn option may be used to warn on any types, attributes,
|
||||||
|
classes, or permissions from a neverallow rule that could not be resolved
|
||||||
|
within the sepolicy file. This can be normal due to differences between
|
||||||
|
the policy from which the neverallow rules were taken and the policy
|
||||||
|
being checked. Such values are ignored for the purposes of neverallow
|
||||||
|
checking.
|
||||||
|
|
||||||
|
The -d or --debug option may be used to cause sepolicy-analyze to emit the
|
||||||
|
neverallow rules as it parses them. This is principally a debugging facility
|
||||||
|
for the parser but could also be used to extract neverallow rules from
|
||||||
|
a full policy.conf file and output them in a more easily parsed format.
|
91
tools/sepolicy-analyze/dups.c
Normal file
91
tools/sepolicy-analyze/dups.c
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "dups.h"
|
||||||
|
|
||||||
|
void dups_usage() {
|
||||||
|
fprintf(stderr, "\tdups\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int find_dups_helper(avtab_key_t * k, avtab_datum_t * d,
|
||||||
|
void *args)
|
||||||
|
{
|
||||||
|
policydb_t *policydb = args;
|
||||||
|
ebitmap_t *sattr, *tattr;
|
||||||
|
ebitmap_node_t *snode, *tnode;
|
||||||
|
unsigned int i, j;
|
||||||
|
avtab_key_t avkey;
|
||||||
|
avtab_ptr_t node;
|
||||||
|
struct type_datum *stype, *ttype, *stype2, *ttype2;
|
||||||
|
bool attrib1, attrib2;
|
||||||
|
|
||||||
|
if (!(k->specified & AVTAB_ALLOWED))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (k->source_type == k->target_type)
|
||||||
|
return 0; /* self rule */
|
||||||
|
|
||||||
|
avkey.target_class = k->target_class;
|
||||||
|
avkey.specified = k->specified;
|
||||||
|
|
||||||
|
sattr = &policydb->type_attr_map[k->source_type - 1];
|
||||||
|
tattr = &policydb->type_attr_map[k->target_type - 1];
|
||||||
|
stype = policydb->type_val_to_struct[k->source_type - 1];
|
||||||
|
ttype = policydb->type_val_to_struct[k->target_type - 1];
|
||||||
|
attrib1 = stype->flavor || ttype->flavor;
|
||||||
|
ebitmap_for_each_bit(sattr, snode, i) {
|
||||||
|
if (!ebitmap_node_get_bit(snode, i))
|
||||||
|
continue;
|
||||||
|
ebitmap_for_each_bit(tattr, tnode, j) {
|
||||||
|
if (!ebitmap_node_get_bit(tnode, j))
|
||||||
|
continue;
|
||||||
|
avkey.source_type = i + 1;
|
||||||
|
avkey.target_type = j + 1;
|
||||||
|
if (avkey.source_type == k->source_type &&
|
||||||
|
avkey.target_type == k->target_type)
|
||||||
|
continue;
|
||||||
|
if (avkey.source_type == avkey.target_type)
|
||||||
|
continue; /* self rule */
|
||||||
|
stype2 = policydb->type_val_to_struct[avkey.source_type - 1];
|
||||||
|
ttype2 = policydb->type_val_to_struct[avkey.target_type - 1];
|
||||||
|
attrib2 = stype2->flavor || ttype2->flavor;
|
||||||
|
if (attrib1 && attrib2)
|
||||||
|
continue; /* overlapping attribute-based rules */
|
||||||
|
for (node = avtab_search_node(&policydb->te_avtab, &avkey);
|
||||||
|
node != NULL;
|
||||||
|
node = avtab_search_node_next(node, avkey.specified)) {
|
||||||
|
uint32_t perms = node->datum.data & d->data;
|
||||||
|
if ((attrib1 && perms == node->datum.data) ||
|
||||||
|
(attrib2 && perms == d->data)) {
|
||||||
|
/*
|
||||||
|
* The attribute-based rule is a superset of the
|
||||||
|
* non-attribute-based rule. This is a dup.
|
||||||
|
*/
|
||||||
|
printf("Duplicate allow rule found:\n");
|
||||||
|
display_allow(policydb, k, i, d->data);
|
||||||
|
display_allow(policydb, &node->key, i, node->datum.data);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int find_dups(policydb_t * policydb)
|
||||||
|
{
|
||||||
|
if (avtab_map(&policydb->te_avtab, find_dups_helper, policydb))
|
||||||
|
return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dups_func (int argc, __attribute__ ((unused)) char **argv, policydb_t *policydb) {
|
||||||
|
if (argc != 1) {
|
||||||
|
USAGE_ERROR = true;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return find_dups(policydb);
|
||||||
|
}
|
11
tools/sepolicy-analyze/dups.h
Normal file
11
tools/sepolicy-analyze/dups.h
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#ifndef DUPS_H
|
||||||
|
#define DUPS_H
|
||||||
|
|
||||||
|
#include <sepol/policydb/policydb.h>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
void dups_usage(void);
|
||||||
|
int dups_func(int argc, char **argv, policydb_t *policydb);
|
||||||
|
|
||||||
|
#endif /* DUPS_H */
|
515
tools/sepolicy-analyze/neverallow.c
Normal file
515
tools/sepolicy-analyze/neverallow.c
Normal file
|
@ -0,0 +1,515 @@
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "neverallow.h"
|
||||||
|
|
||||||
|
static int debug;
|
||||||
|
static int warn;
|
||||||
|
|
||||||
|
void neverallow_usage() {
|
||||||
|
fprintf(stderr, "\tneverallow [-w|--warn] [-d|--debug] [-n|--neverallows <neverallow-rules>] | [-f|--file <neverallow-file>]\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read_typeset(policydb_t *policydb, char **ptr, char *end,
|
||||||
|
type_set_t *typeset, uint32_t *flags)
|
||||||
|
{
|
||||||
|
const char *keyword = "self";
|
||||||
|
size_t keyword_size = strlen(keyword), len;
|
||||||
|
char *p = *ptr;
|
||||||
|
unsigned openparens = 0;
|
||||||
|
char *start, *id;
|
||||||
|
type_datum_t *type;
|
||||||
|
struct ebitmap_node *n;
|
||||||
|
unsigned int bit;
|
||||||
|
bool negate = false;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
do {
|
||||||
|
while (p < end && isspace(*p))
|
||||||
|
p++;
|
||||||
|
|
||||||
|
if (p == end)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (*p == '~') {
|
||||||
|
if (debug)
|
||||||
|
printf(" ~");
|
||||||
|
typeset->flags = TYPE_COMP;
|
||||||
|
p++;
|
||||||
|
while (p < end && isspace(*p))
|
||||||
|
p++;
|
||||||
|
if (p == end)
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*p == '{') {
|
||||||
|
if (debug && !openparens)
|
||||||
|
printf(" {");
|
||||||
|
openparens++;
|
||||||
|
p++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*p == '}') {
|
||||||
|
if (debug && openparens == 1)
|
||||||
|
printf(" }");
|
||||||
|
if (openparens == 0)
|
||||||
|
goto err;
|
||||||
|
openparens--;
|
||||||
|
p++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*p == '*') {
|
||||||
|
if (debug)
|
||||||
|
printf(" *");
|
||||||
|
typeset->flags = TYPE_STAR;
|
||||||
|
p++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*p == '-') {
|
||||||
|
if (debug)
|
||||||
|
printf(" -");
|
||||||
|
negate = true;
|
||||||
|
p++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*p == '#') {
|
||||||
|
while (p < end && *p != '\n')
|
||||||
|
p++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
start = p;
|
||||||
|
while (p < end && !isspace(*p) && *p != ':' && *p != ';' && *p != '{' && *p != '}' && *p != '#')
|
||||||
|
p++;
|
||||||
|
|
||||||
|
if (p == start)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
len = p - start;
|
||||||
|
if (len == keyword_size && !strncmp(start, keyword, keyword_size)) {
|
||||||
|
if (debug)
|
||||||
|
printf(" self");
|
||||||
|
*flags |= RULE_SELF;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
id = calloc(1, len + 1);
|
||||||
|
if (!id)
|
||||||
|
goto err;
|
||||||
|
memcpy(id, start, len);
|
||||||
|
if (debug)
|
||||||
|
printf(" %s", id);
|
||||||
|
type = hashtab_search(policydb->p_types.table, id);
|
||||||
|
if (!type) {
|
||||||
|
if (warn)
|
||||||
|
fprintf(stderr, "Warning! Type or attribute %s used in neverallow undefined in policy being checked.\n", id);
|
||||||
|
negate = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
free(id);
|
||||||
|
|
||||||
|
if (type->flavor == TYPE_ATTRIB) {
|
||||||
|
if (negate)
|
||||||
|
rc = ebitmap_union(&typeset->negset, &policydb->attr_type_map[type->s.value - 1]);
|
||||||
|
else
|
||||||
|
rc = ebitmap_union(&typeset->types, &policydb->attr_type_map[type->s.value - 1]);
|
||||||
|
} else if (negate) {
|
||||||
|
rc = ebitmap_set_bit(&typeset->negset, type->s.value - 1, 1);
|
||||||
|
} else {
|
||||||
|
rc = ebitmap_set_bit(&typeset->types, type->s.value - 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
negate = false;
|
||||||
|
|
||||||
|
if (rc)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
} while (p < end && openparens);
|
||||||
|
|
||||||
|
if (p == end)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (typeset->flags & TYPE_STAR) {
|
||||||
|
for (bit = 0; bit < policydb->p_types.nprim; bit++) {
|
||||||
|
if (ebitmap_get_bit(&typeset->negset, bit))
|
||||||
|
continue;
|
||||||
|
if (policydb->type_val_to_struct[bit] &&
|
||||||
|
policydb->type_val_to_struct[bit]->flavor == TYPE_ATTRIB)
|
||||||
|
continue;
|
||||||
|
if (ebitmap_set_bit(&typeset->types, bit, 1))
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ebitmap_for_each_bit(&typeset->negset, n, bit) {
|
||||||
|
if (!ebitmap_node_get_bit(n, bit))
|
||||||
|
continue;
|
||||||
|
if (ebitmap_set_bit(&typeset->types, bit, 0))
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeset->flags & TYPE_COMP) {
|
||||||
|
for (bit = 0; bit < policydb->p_types.nprim; bit++) {
|
||||||
|
if (policydb->type_val_to_struct[bit] &&
|
||||||
|
policydb->type_val_to_struct[bit]->flavor == TYPE_ATTRIB)
|
||||||
|
continue;
|
||||||
|
if (ebitmap_get_bit(&typeset->types, bit))
|
||||||
|
ebitmap_set_bit(&typeset->types, bit, 0);
|
||||||
|
else {
|
||||||
|
if (ebitmap_set_bit(&typeset->types, bit, 1))
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (warn && ebitmap_length(&typeset->types) == 0 && !(*flags))
|
||||||
|
fprintf(stderr, "Warning! Empty type set\n");
|
||||||
|
|
||||||
|
*ptr = p;
|
||||||
|
return 0;
|
||||||
|
err:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read_classperms(policydb_t *policydb, char **ptr, char *end,
|
||||||
|
class_perm_node_t **perms)
|
||||||
|
{
|
||||||
|
char *p = *ptr;
|
||||||
|
unsigned openparens = 0;
|
||||||
|
char *id, *start;
|
||||||
|
class_datum_t *cls = NULL;
|
||||||
|
perm_datum_t *perm = NULL;
|
||||||
|
class_perm_node_t *classperms = NULL, *node = NULL;
|
||||||
|
bool complement = false;
|
||||||
|
|
||||||
|
while (p < end && isspace(*p))
|
||||||
|
p++;
|
||||||
|
|
||||||
|
if (p == end || *p != ':')
|
||||||
|
goto err;
|
||||||
|
p++;
|
||||||
|
|
||||||
|
if (debug)
|
||||||
|
printf(" :");
|
||||||
|
|
||||||
|
do {
|
||||||
|
while (p < end && isspace(*p))
|
||||||
|
p++;
|
||||||
|
|
||||||
|
if (p == end)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (*p == '{') {
|
||||||
|
if (debug && !openparens)
|
||||||
|
printf(" {");
|
||||||
|
openparens++;
|
||||||
|
p++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*p == '}') {
|
||||||
|
if (debug && openparens == 1)
|
||||||
|
printf(" }");
|
||||||
|
if (openparens == 0)
|
||||||
|
goto err;
|
||||||
|
openparens--;
|
||||||
|
p++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*p == '#') {
|
||||||
|
while (p < end && *p != '\n')
|
||||||
|
p++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
start = p;
|
||||||
|
while (p < end && !isspace(*p) && *p != '{' && *p != '}' && *p != ';' && *p != '#')
|
||||||
|
p++;
|
||||||
|
|
||||||
|
if (p == start)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
id = calloc(1, p - start + 1);
|
||||||
|
if (!id)
|
||||||
|
goto err;
|
||||||
|
memcpy(id, start, p - start);
|
||||||
|
if (debug)
|
||||||
|
printf(" %s", id);
|
||||||
|
cls = hashtab_search(policydb->p_classes.table, id);
|
||||||
|
if (!cls) {
|
||||||
|
if (warn)
|
||||||
|
fprintf(stderr, "Warning! Class %s used in neverallow undefined in policy being checked.\n", id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
node = calloc(1, sizeof *node);
|
||||||
|
if (!node)
|
||||||
|
goto err;
|
||||||
|
node->class = cls->s.value;
|
||||||
|
node->next = classperms;
|
||||||
|
classperms = node;
|
||||||
|
free(id);
|
||||||
|
} while (p < end && openparens);
|
||||||
|
|
||||||
|
if (p == end)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (warn && !classperms)
|
||||||
|
fprintf(stderr, "Warning! Empty class set\n");
|
||||||
|
|
||||||
|
do {
|
||||||
|
while (p < end && isspace(*p))
|
||||||
|
p++;
|
||||||
|
|
||||||
|
if (p == end)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (*p == '~') {
|
||||||
|
if (debug)
|
||||||
|
printf(" ~");
|
||||||
|
complement = true;
|
||||||
|
p++;
|
||||||
|
while (p < end && isspace(*p))
|
||||||
|
p++;
|
||||||
|
if (p == end)
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*p == '{') {
|
||||||
|
if (debug && !openparens)
|
||||||
|
printf(" {");
|
||||||
|
openparens++;
|
||||||
|
p++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*p == '}') {
|
||||||
|
if (debug && openparens == 1)
|
||||||
|
printf(" }");
|
||||||
|
if (openparens == 0)
|
||||||
|
goto err;
|
||||||
|
openparens--;
|
||||||
|
p++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*p == '#') {
|
||||||
|
while (p < end && *p != '\n')
|
||||||
|
p++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
start = p;
|
||||||
|
while (p < end && !isspace(*p) && *p != '{' && *p != '}' && *p != ';' && *p != '#')
|
||||||
|
p++;
|
||||||
|
|
||||||
|
if (p == start)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
id = calloc(1, p - start + 1);
|
||||||
|
if (!id)
|
||||||
|
goto err;
|
||||||
|
memcpy(id, start, p - start);
|
||||||
|
if (debug)
|
||||||
|
printf(" %s", id);
|
||||||
|
|
||||||
|
if (!strcmp(id, "*")) {
|
||||||
|
for (node = classperms; node; node = node->next)
|
||||||
|
node->data = ~0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (node = classperms; node; node = node->next) {
|
||||||
|
cls = policydb->class_val_to_struct[node->class-1];
|
||||||
|
perm = hashtab_search(cls->permissions.table, id);
|
||||||
|
if (cls->comdatum && !perm)
|
||||||
|
perm = hashtab_search(cls->comdatum->permissions.table, id);
|
||||||
|
if (!perm) {
|
||||||
|
if (warn)
|
||||||
|
fprintf(stderr, "Warning! Permission %s used in neverallow undefined in class %s in policy being checked.\n", id, policydb->p_class_val_to_name[node->class-1]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
node->data |= 1U << (perm->s.value - 1);
|
||||||
|
}
|
||||||
|
free(id);
|
||||||
|
} while (p < end && openparens);
|
||||||
|
|
||||||
|
if (p == end)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (complement) {
|
||||||
|
for (node = classperms; node; node = node->next)
|
||||||
|
node->data = ~node->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (warn) {
|
||||||
|
for (node = classperms; node; node = node->next)
|
||||||
|
if (!node->data)
|
||||||
|
fprintf(stderr, "Warning! Empty permission set\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
*perms = classperms;
|
||||||
|
*ptr = p;
|
||||||
|
return 0;
|
||||||
|
err:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int check_neverallows(policydb_t *policydb, char *text, char *end)
|
||||||
|
{
|
||||||
|
const char *keyword = "neverallow";
|
||||||
|
size_t keyword_size = strlen(keyword), len;
|
||||||
|
struct avrule *neverallows = NULL, *avrule;
|
||||||
|
char *p, *start;
|
||||||
|
|
||||||
|
p = text;
|
||||||
|
while (p < end) {
|
||||||
|
while (p < end && isspace(*p))
|
||||||
|
p++;
|
||||||
|
|
||||||
|
if (*p == '#') {
|
||||||
|
while (p < end && *p != '\n')
|
||||||
|
p++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
start = p;
|
||||||
|
while (p < end && !isspace(*p))
|
||||||
|
p++;
|
||||||
|
|
||||||
|
len = p - start;
|
||||||
|
if (len != keyword_size || strncmp(start, keyword, keyword_size))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (debug)
|
||||||
|
printf("neverallow");
|
||||||
|
|
||||||
|
avrule = calloc(1, sizeof *avrule);
|
||||||
|
if (!avrule)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
avrule->specified = AVRULE_NEVERALLOW;
|
||||||
|
|
||||||
|
if (read_typeset(policydb, &p, end, &avrule->stypes, &avrule->flags))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (read_typeset(policydb, &p, end, &avrule->ttypes, &avrule->flags))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (read_classperms(policydb, &p, end, &avrule->perms))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
while (p < end && *p != ';')
|
||||||
|
p++;
|
||||||
|
|
||||||
|
if (p == end || *p != ';')
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (debug)
|
||||||
|
printf(";\n");
|
||||||
|
|
||||||
|
avrule->next = neverallows;
|
||||||
|
neverallows = avrule;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!neverallows)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
return check_assertions(NULL, policydb, neverallows);
|
||||||
|
err:
|
||||||
|
if (errno == ENOMEM) {
|
||||||
|
fprintf(stderr, "Out of memory while parsing neverallow rules\n");
|
||||||
|
} else
|
||||||
|
fprintf(stderr, "Error while parsing neverallow rules\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int check_neverallows_file(policydb_t *policydb, const char *filename)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
struct stat sb;
|
||||||
|
char *text, *end;
|
||||||
|
|
||||||
|
fd = open(filename, O_RDONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
fprintf(stderr, "Could not 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;
|
||||||
|
}
|
||||||
|
text = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||||
|
end = text + sb.st_size;
|
||||||
|
if (text == MAP_FAILED) {
|
||||||
|
fprintf(stderr, "Can't mmap '%s': %s\n", filename, strerror(errno));
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
return check_neverallows(policydb, text, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int check_neverallows_string(policydb_t *policydb, char *string, size_t len)
|
||||||
|
{
|
||||||
|
char *text, *end;
|
||||||
|
text = string;
|
||||||
|
end = text + len;
|
||||||
|
return check_neverallows(policydb, text, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
int neverallow_func (int argc, char **argv, policydb_t *policydb) {
|
||||||
|
char *rules = 0, *file = 0;
|
||||||
|
char ch;
|
||||||
|
|
||||||
|
struct option neverallow_options[] = {
|
||||||
|
{"debug", no_argument, NULL, 'd'},
|
||||||
|
{"file_input", required_argument, NULL, 'f'},
|
||||||
|
{"neverallow", required_argument, NULL, 'n'},
|
||||||
|
{"warn", no_argument, NULL, 'w'},
|
||||||
|
{NULL, 0, NULL, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
while ((ch = getopt_long(argc, argv, "df:n:w", neverallow_options, NULL)) != -1) {
|
||||||
|
switch (ch) {
|
||||||
|
case 'd':
|
||||||
|
debug = 1;
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
file = optarg;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
rules = optarg;
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
warn = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
USAGE_ERROR = true;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(rules || file) || (rules && file)){
|
||||||
|
USAGE_ERROR = true;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (file) {
|
||||||
|
return check_neverallows_file(policydb, file);
|
||||||
|
} else {
|
||||||
|
return check_neverallows_string(policydb, rules, strlen(rules));
|
||||||
|
}
|
||||||
|
}
|
11
tools/sepolicy-analyze/neverallow.h
Normal file
11
tools/sepolicy-analyze/neverallow.h
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#ifndef NEVERALLOW_H
|
||||||
|
#define NEVERALLOW_H
|
||||||
|
|
||||||
|
#include <sepol/policydb/policydb.h>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
void neverallow_usage(void);
|
||||||
|
int neverallow_func(int argc, char **argv, policydb_t *policydb);
|
||||||
|
|
||||||
|
#endif /* NEVERALLOW_H */
|
30
tools/sepolicy-analyze/perm.c
Normal file
30
tools/sepolicy-analyze/perm.c
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#include "perm.h"
|
||||||
|
|
||||||
|
void permissive_usage() {
|
||||||
|
fprintf(stderr, "\tpermissive\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int list_permissive(policydb_t * policydb)
|
||||||
|
{
|
||||||
|
struct ebitmap_node *n;
|
||||||
|
unsigned int bit;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* iterate over all domains and check if domain is in permissive
|
||||||
|
*/
|
||||||
|
ebitmap_for_each_bit(&policydb->permissive_map, n, bit)
|
||||||
|
{
|
||||||
|
if (ebitmap_node_get_bit(n, bit)) {
|
||||||
|
printf("%s\n", policydb->p_type_val_to_name[bit -1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int permissive_func (int argc, __attribute__ ((unused)) char **argv, policydb_t *policydb) {
|
||||||
|
if (argc != 1) {
|
||||||
|
USAGE_ERROR = true;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return list_permissive(policydb);
|
||||||
|
}
|
11
tools/sepolicy-analyze/perm.h
Normal file
11
tools/sepolicy-analyze/perm.h
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#ifndef PERM_H
|
||||||
|
#define PERM_H
|
||||||
|
|
||||||
|
#include <sepol/policydb/policydb.h>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
void permissive_usage(void);
|
||||||
|
int permissive_func(int argc, char **argv, policydb_t *policydb);
|
||||||
|
|
||||||
|
#endif /* PERM_H */
|
61
tools/sepolicy-analyze/sepolicy-analyze.c
Normal file
61
tools/sepolicy-analyze/sepolicy-analyze.c
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "dups.h"
|
||||||
|
#include "neverallow.h"
|
||||||
|
#include "perm.h"
|
||||||
|
#include "typecmp.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#define NUM_COMPONENTS (int) (sizeof(analyze_components)/sizeof(analyze_components[0]))
|
||||||
|
|
||||||
|
#define COMP(x) { #x, sizeof(#x) - 1, x ##_usage, x ##_func }
|
||||||
|
static struct {
|
||||||
|
const char *key;
|
||||||
|
size_t keylen;
|
||||||
|
void (*usage) (void);
|
||||||
|
int (*func) (int argc, char **argv, policydb_t *policydb);
|
||||||
|
} analyze_components[] = {
|
||||||
|
COMP(dups),
|
||||||
|
COMP(neverallow),
|
||||||
|
COMP(permissive),
|
||||||
|
COMP(typecmp)
|
||||||
|
};
|
||||||
|
|
||||||
|
void usage(char *arg0)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
fprintf(stderr, "%s must be called on a policy file with a component and the appropriate arguments specified\n", arg0);
|
||||||
|
fprintf(stderr, "%s <policy-file>:\n", arg0);
|
||||||
|
for(i = 0; i < NUM_COMPONENTS; i++) {
|
||||||
|
analyze_components[i].usage();
|
||||||
|
}
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
char *policy;
|
||||||
|
struct policy_file pf;
|
||||||
|
policydb_t policydb;
|
||||||
|
int rc, i;
|
||||||
|
if (argc < 3)
|
||||||
|
usage(argv[0]);
|
||||||
|
policy = argv[1];
|
||||||
|
if(load_policy(policy, &policydb, &pf))
|
||||||
|
exit(1);
|
||||||
|
for(i = 0; i < NUM_COMPONENTS; i++) {
|
||||||
|
if (!strcmp(analyze_components[i].key, argv[2])) {
|
||||||
|
rc = analyze_components[i].func(argc - 2, argv + 2, &policydb);
|
||||||
|
if (rc && USAGE_ERROR) {
|
||||||
|
usage(argv[0]); }
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
usage(argv[0]);
|
||||||
|
|
||||||
|
// will never be called due to exit() call in usage
|
||||||
|
exit(1);
|
||||||
|
}
|
295
tools/sepolicy-analyze/typecmp.c
Normal file
295
tools/sepolicy-analyze/typecmp.c
Normal file
|
@ -0,0 +1,295 @@
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <sepol/policydb/expand.h>
|
||||||
|
|
||||||
|
#include "typecmp.h"
|
||||||
|
|
||||||
|
void typecmp_usage() {
|
||||||
|
fprintf(stderr, "\ttypecmp [-d|--diff] [-e|--equiv]\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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->key, idx1, perms1);
|
||||||
|
if (perms2)
|
||||||
|
display_allow(policydb, &c->key, idx2, perms2);
|
||||||
|
printf("\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int analyze_types(policydb_t * policydb, char diff, char equiv)
|
||||||
|
{
|
||||||
|
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->key, i, l1->datum.data);
|
||||||
|
if (l2)
|
||||||
|
display_allow(policydb, &l2->key, 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 typecmp_func (int argc, char **argv, policydb_t *policydb) {
|
||||||
|
char ch, diff = 0, equiv = 0;
|
||||||
|
|
||||||
|
struct option typecmp_options[] = {
|
||||||
|
{"diff", no_argument, NULL, 'd'},
|
||||||
|
{"equiv", no_argument, NULL, 'e'},
|
||||||
|
{NULL, 0, NULL, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
while ((ch = getopt_long(argc, argv, "de", typecmp_options, NULL)) != -1) {
|
||||||
|
switch (ch) {
|
||||||
|
case 'd':
|
||||||
|
diff = 1;
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
equiv = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
USAGE_ERROR = true;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(diff || equiv)) {
|
||||||
|
USAGE_ERROR = true;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return analyze_types(policydb, diff, equiv);
|
||||||
|
}
|
11
tools/sepolicy-analyze/typecmp.h
Normal file
11
tools/sepolicy-analyze/typecmp.h
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#ifndef TYPECMP_H
|
||||||
|
#define TYPECMP_H
|
||||||
|
|
||||||
|
#include <sepol/policydb/policydb.h>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
void typecmp_usage(void);
|
||||||
|
int typecmp_func(int argc, char **argv, policydb_t *policydb);
|
||||||
|
|
||||||
|
#endif /* TYPECMP_H */
|
68
tools/sepolicy-analyze/utils.c
Normal file
68
tools/sepolicy-analyze/utils.c
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sepol/policydb/policydb.h>
|
||||||
|
#include <sepol/policydb/util.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
bool USAGE_ERROR = false;
|
||||||
|
|
||||||
|
void display_allow(policydb_t *policydb, avtab_key_t *key, int idx, uint32_t perms)
|
||||||
|
{
|
||||||
|
printf(" allow %s %s:%s { %s };\n",
|
||||||
|
policydb->p_type_val_to_name[key->source_type
|
||||||
|
? key->source_type - 1 : idx],
|
||||||
|
key->target_type == key->source_type ? "self" :
|
||||||
|
policydb->p_type_val_to_name[key->target_type
|
||||||
|
? key->target_type - 1 : idx],
|
||||||
|
policydb->p_class_val_to_name[key->target_class - 1],
|
||||||
|
sepol_av_to_string
|
||||||
|
(policydb, key->target_class, perms));
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
16
tools/sepolicy-analyze/utils.h
Normal file
16
tools/sepolicy-analyze/utils.h
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#ifndef UTILS_H
|
||||||
|
#define UTILS_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sepol/policydb/avtab.h>
|
||||||
|
#include <sepol/policydb/policydb.h>
|
||||||
|
|
||||||
|
|
||||||
|
extern bool USAGE_ERROR;
|
||||||
|
|
||||||
|
void display_allow(policydb_t *policydb, avtab_key_t *key, int idx, uint32_t perms);
|
||||||
|
|
||||||
|
int load_policy(char *filename, policydb_t * policydb, struct policy_file *pf);
|
||||||
|
|
||||||
|
#endif /* UTILS_H */
|
Loading…
Reference in a new issue