aboutsummaryrefslogtreecommitdiffstats
path: root/tools/sepolicy-analyze/neverallow.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/sepolicy-analyze/neverallow.c')
-rw-r--r--tools/sepolicy-analyze/neverallow.c515
1 files changed, 515 insertions, 0 deletions
diff --git a/tools/sepolicy-analyze/neverallow.c b/tools/sepolicy-analyze/neverallow.c
new file mode 100644
index 0000000..1da88c0
--- /dev/null
+++ b/tools/sepolicy-analyze/neverallow.c
@@ -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));
+ }
+}