aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Smalley <sds@tycho.nsa.gov>2015-08-28 03:57:23 +0000
committerAndroid Git Automerger <android-git-automerger@android.com>2015-08-28 03:57:23 +0000
commitb5dd69a1aa92cf1b9f2ca9d65f66fc31b0b54db6 (patch)
tree100951f47e224f8fb593a7264ff41f37a49d2a9c
parent57dec60cf3e1a59ad397424047c91c49cba2fef6 (diff)
parentc423b1aae888296edc70dc4367d93a1314c61fa9 (diff)
downloadandroid_external_sepolicy-b5dd69a1aa92cf1b9f2ca9d65f66fc31b0b54db6.tar.gz
android_external_sepolicy-b5dd69a1aa92cf1b9f2ca9d65f66fc31b0b54db6.tar.bz2
android_external_sepolicy-b5dd69a1aa92cf1b9f2ca9d65f66fc31b0b54db6.zip
am c423b1aa: Add neverallow checking to sepolicy-analyze.
* commit 'c423b1aae888296edc70dc4367d93a1314c61fa9': Add neverallow checking to sepolicy-analyze.
-rw-r--r--tools/README34
-rw-r--r--tools/sepolicy-analyze.c468
2 files changed, 497 insertions, 5 deletions
diff --git a/tools/README b/tools/README
index 8a8dce1..2aa520a 100644
--- a/tools/README
+++ b/tools/README
@@ -94,3 +94,37 @@ sepolicy-analyze
-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.
diff --git a/tools/sepolicy-analyze.c b/tools/sepolicy-analyze.c
index c9dab81..afebd69 100644
--- a/tools/sepolicy-analyze.c
+++ b/tools/sepolicy-analyze.c
@@ -12,10 +12,14 @@
#include <sepol/policydb/expand.h>
#include <sepol/policydb/util.h>
#include <stdbool.h>
+#include <ctype.h>
+
+static int debug;
+static int warn;
void usage(char *arg0)
{
- fprintf(stderr, "%s [-e|--equiv] [-d|--diff] [-D|--dups] [-p|--permissive] -P <policy file>\n", 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);
}
@@ -425,24 +429,466 @@ static int list_permissive(policydb_t * policydb)
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;
+ 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, "edDpP:", long_options, NULL)) != -1) {
+ while ((ch = getopt_long(argc, argv, "edDpn:P:wz", long_options, NULL)) != -1) {
switch (ch) {
case 'e':
equiv = 1;
@@ -453,18 +899,27 @@ int main(int argc, char **argv)
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))
+ if (!policy || (!equiv && !diff && !dups && !permissive && !neverallows))
usage(argv[0]);
if (load_policy(policy, &policydb, &pf))
@@ -479,7 +934,10 @@ int main(int argc, char **argv)
if (permissive)
list_permissive(&policydb);
+ if (neverallows)
+ rc |= check_neverallows(&policydb, neverallows);
+
policydb_destroy(&policydb);
- return 0;
+ return rc;
}