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 $(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)
|
||||
include $(call all-makefiles-under,$(LOCAL_PATH))
|
79
tools/README
79
tools/README
|
@ -50,81 +50,4 @@ sepolicy-check
|
|||
|
||||
sepolicy-analyze
|
||||
A tool for performing various kinds of analysis on a sepolicy
|
||||
file. The current kinds of analysis that are currently supported
|
||||
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.
|
||||
file.
|
|
@ -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