0de2b45f63
Commit dc0ab516f11d8e2c413315e733e25a41ba468e4f changed the libsepol structures on which sepolicy-analyze relies so that it could be compiled as a C++ library. Reflect this change in sepolicy-analyze. Change-Id: I7da601767c3a4ebed7274e33304d8b589a9115fe
515 lines
13 KiB
C
515 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
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->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));
|
|
}
|
|
}
|