android_system_sepolicy/tools/sepolicy-check.c
Stephen Smalley 7b2bee99c1 Add sepolicy-analyze tool.
And also remove the unnecessary references to libselinux for
sepolicy-check, as it has no dependencies on libselinux.
Also enable -Wall -Werror on building all of these tools and
fix up all such errors.

Usage:
$ sepolicy-analyze -e -P out/target/product/<device>/root/sepolicy
or
$ sepolicy-analyze -d -P out/target/product/<device>/root/sepolicy

The first form will display all type pairs that are "equivalent", i.e.
they are identical with respect to allow rules, including indirect allow
rules via attributes and default-enabled conditional rules (i.e. default
boolean values yield a true conditional expression).

Equivalent types are candidates for being coalesced into a single type.
However, there may be legitimate reasons for them to remain separate,
for example:
- the types may differ in a respect not included in the current
analysis, such as default-disabled conditional rules, audit-related
rules (auditallow or dontaudit), default type transitions, or
constraints (e.g. mls), or
- the current policy may be overly permissive with respect to one or the
other of the types and thus the correct action may be to tighten access
to one or the other rather than coalescing them together, or
- the domains that would in fact have different accesses to the types
may not yet be defined or may be unconfined in the policy you are
analyzing (e.g. in AOSP policy).

The second form will display type pairs that differ and the first
difference found between the two types.  This output can be long.

We have plans to explore further enhancements to this tool, including
support for identifying isomorphic types.  That will be required to
identify similar domains since all domains differ in at least their
entrypoint type and in their tmpfs type and thus will never show up as
equivalent even if they are in all other respects identical to each other.

Change-Id: If0ee00188469d2a1e165fdd52f235c705d22cd4e
Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov>
2013-10-31 15:12:32 -04:00

297 lines
6.6 KiB
C

#include <getopt.h>
#include <unistd.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>
#define EQUALS 0
#define NOT 1
#define ANY 2
void usage(char *arg0) {
fprintf(stderr, "%s -s <source> -t <target> -c <class> -p <perm> -P <policy file>\n", arg0);
exit(1);
}
void *cmalloc(size_t s) {
void *t = malloc(s);
if (t == NULL) {
fprintf(stderr, "Out of memory\n");
exit(1);
}
return t;
}
int parse_ops(char **arg) {
switch (*arg[0]) {
case '-':
*arg = *arg + 1;
return NOT;
case '*':
return ANY;
default:
return EQUALS;
}
}
int check(int op, uint16_t arg1, uint16_t arg2) {
switch (op) {
case EQUALS:
return arg1 == arg2;
case NOT:
return arg1 != arg2;
case ANY:
return 1;
default:
fprintf(stderr, "Bad op while checking!");
return 2;
}
}
int check_perm(avtab_ptr_t current, perm_datum_t *perm) {
uint16_t perm_bitmask = 1U << (perm->s.value - 1);
return (current->datum.data & perm_bitmask) != 0;
}
int expand_and_check(int s_op, uint32_t source_type,
int t_op, uint32_t target_type,
int c_op, uint32_t target_class,
perm_datum_t *perm, policydb_t *policy, avtab_t *avtab) {
avtab_t exp_avtab;
avtab_ptr_t cur;
unsigned int i;
int match;
if (avtab_init(&exp_avtab)) {
fputs("out of memory\n", stderr);
return -1;
}
if (expand_avtab(policy, avtab, &exp_avtab)) {
fputs("out of memory\n", stderr);
avtab_destroy(&exp_avtab);
return -1;
}
for (i = 0; i < exp_avtab.nslot; i++) {
for (cur = exp_avtab.htable[i]; cur; cur = cur->next) {
match = 1;
match &= check(s_op, source_type, cur->key.source_type);
match &= check(t_op, target_type, cur->key.target_type);
match &= check(c_op, target_class, cur->key.target_class);
match &= check_perm(cur, perm);
if (match) {
avtab_destroy(&exp_avtab);
return 1;
}
}
}
avtab_destroy(&exp_avtab);
return 0;
}
/*
* Checks to see if a rule matching the given arguments already exists.
*
* The format for the arguments is as follows:
*
* - A bare string is treated as a literal and will be matched by equality.
* - A string starting with "-" will be matched by inequality.
* - A string starting with "*" will be treated as a wildcard.
*
* The return codes for this function are as follows:
*
* - 0 indicates a successful return without a match
* - 1 indicates a successful return with a match
* - -1 indicates an error
*/
int check_rule(char *s, char *t, char *c, char *p, policydb_t *policy) {
type_datum_t *src = NULL;
type_datum_t *tgt = NULL;
class_datum_t *cls = NULL;
perm_datum_t *perm = NULL;
int s_op = parse_ops(&s);
int t_op = parse_ops(&t);
int c_op = parse_ops(&c);
int p_op = parse_ops(&p);
avtab_key_t key;
int match;
key.source_type = key.target_type = key.target_class = 0;
if (s_op != ANY) {
src = hashtab_search(policy->p_types.table, s);
if (src == NULL) {
fprintf(stderr, "source type %s does not exist\n", s);
return -1;
}
}
if (t_op != ANY) {
tgt = hashtab_search(policy->p_types.table, t);
if (tgt == NULL) {
fprintf(stderr, "target type %s does not exist\n", t);
return -1;
}
}
if (c_op != ANY) {
cls = hashtab_search(policy->p_classes.table, c);
if (cls == NULL) {
fprintf(stderr, "class %s does not exist\n", c);
return -1;
}
}
if (p_op != ANY) {
perm = hashtab_search(cls->permissions.table, p);
if (perm == NULL) {
if (cls->comdatum == NULL) {
fprintf(stderr, "perm %s does not exist in class %s\n", p, c);
return -1;
}
perm = hashtab_search(cls->comdatum->permissions.table, p);
if (perm == NULL) {
fprintf(stderr, "perm %s does not exist in class %s\n", p, c);
return -1;
}
}
}
if (s_op != ANY)
key.source_type = src->s.value;
if (t_op != ANY)
key.target_type = tgt->s.value;
if (c_op != ANY)
key.target_class = cls->s.value;
/* Check unconditional rules after attribute expansion. */
match = expand_and_check(s_op, key.source_type,
t_op, key.target_type,
c_op, key.target_class,
perm, policy, &policy->te_avtab);
if (match)
return match;
/* Check conditional rules after attribute expansion. */
return expand_and_check(s_op, key.source_type,
t_op, key.target_type,
c_op, key.target_class,
perm, policy, &policy->te_cond_avtab);
}
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;
}
int main(int argc, char **argv)
{
char *policy = NULL, *source = NULL, *target = NULL, *class = NULL, *perm = NULL;
policydb_t policydb;
struct policy_file pf;
sidtab_t sidtab;
char ch;
int match = 1;
struct option long_options[] = {
{"source", required_argument, NULL, 's'},
{"target", required_argument, NULL, 't'},
{"class", required_argument, NULL, 'c'},
{"perm", required_argument, NULL, 'p'},
{"policy", required_argument, NULL, 'P'},
{NULL, 0, NULL, 0}
};
while ((ch = getopt_long(argc, argv, "s:t:c:p:P:", long_options, NULL)) != -1) {
switch (ch) {
case 's':
source = optarg;
break;
case 't':
target = optarg;
break;
case 'c':
class = optarg;
break;
case 'p':
perm = optarg;
break;
case 'P':
policy = optarg;
break;
default:
usage(argv[0]);
}
}
if (!source || !target || !class || !perm || !policy)
usage(argv[0]);
sepol_set_policydb(&policydb);
sepol_set_sidtab(&sidtab);
if (load_policy(policy, &policydb, &pf))
goto out;
match = check_rule(source, target, class, perm, &policydb);
if (match < 0) {
fprintf(stderr, "Error checking rules!\n");
goto out;
} else if (match > 0) {
printf("Match found!\n");
goto out;
}
match = 0;
out:
policydb_destroy(&policydb);
return match;
}