android_system_sepolicy/tools/sepolicy-analyze/neverallow.c
Alex Klyubin c60d3ea164 Do not warn about empty typesets in neverallows
Empty typeset is not an issue in neverallow rules. The reason is that
it's completly normal for scontext or tcontext of neverallow rules to
evaluate to an empty type set. For example, there are neverallow rules
whose purpose is to test that all types with particular powers are
associated with a particular attribute:
  neverallow {
    untrusted_app_all
    -untrusted_app
    -untrusted_app_25
  } domain:process fork;

Test: sepolicy-analyze neverallow -w -n \
          'neverallow {} {}:binder call;'
      produces empty output instead of "Warning!  Empty type set"
Bug: 37357742
Change-Id: Id61b4fe22fafaf0522d8769dd4e23dfde6cd9f45
2017-04-25 14:25:04 -07:00

513 lines
13 KiB
C

#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;
}
}
}
*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->tclass = 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->tclass-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->tclass-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));
}
}